Prevents automatic show,stat,.. updates on search errors
[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 (! validateUpdateRequest(commands)) {          \r
168           return "0";\r
169         } else {\r
170           logger.debug("Processing request for " + commands); \r
171           List<CommandThread> threadList = new ArrayList<CommandThread>();\r
172           StringTokenizer tokens = new StringTokenizer(commands,",");\r
173           while (tokens.hasMoreElements()) {          \r
174             threadList.add(new CommandThread(pzreq.getCommand(tokens.nextToken()),searchClient));            \r
175           }\r
176           for (CommandThread thread : threadList) {\r
177             thread.start();\r
178           }\r
179           for (CommandThread thread : threadList) {\r
180             try {\r
181               thread.join();\r
182             } catch (InterruptedException e) {\r
183               e.printStackTrace();\r
184             }\r
185           }\r
186           for (CommandThread thread : threadList) {\r
187              String commandName = thread.getCommand().getCommandName();\r
188              ClientCommandResponse response = (ClientCommandResponse) thread.getCommandResponse();\r
189              responseLogger.debug("Response was: " + response.getResponseString());\r
190              ResponseDataObject responseObject = ResponseParser.getParser().getDataObject(response);\r
191              if (ResponseParser.docTypes.contains(responseObject.getType())) {\r
192                pzresp.put(commandName, responseObject);\r
193              } else {\r
194                if (commandName.equals("record") && \r
195                    (pzreq.getRecord().hasParameterValue("offset") ||\r
196                     pzreq.getRecord().hasParameterValue("checksum"))) {\r
197                  RecordResponse recordResponse = new RecordResponse();\r
198                  recordResponse.setType("record");\r
199                  recordResponse.setXml(responseObject.getXml());\r
200                  recordResponse.setAttribute("activeclients", "0");\r
201                  pzresp.put(commandName, recordResponse);\r
202                }\r
203              }\r
204           }\r
205           if (commands.equals("record")) {\r
206             return pzresp.getRecord().getActiveClients();\r
207           } else {\r
208             return pzresp.getActiveClients();\r
209           }\r
210         }\r
211       }  \r
212     } catch (ClassCastException cce) {\r
213       cce.printStackTrace();    \r
214       return "";\r
215     } catch (NullPointerException npe) {\r
216       npe.printStackTrace();\r
217       return "";\r
218     } catch (Exception e) {\r
219       e.printStackTrace();\r
220       return "";\r
221     }\r
222     \r
223   }\r
224   \r
225   public boolean commandsAreValid(String commands) {\r
226     if (commands.equals("record")) {\r
227       if (!pzreq.getCommand("record").hasParameterValue("id")) {\r
228         logger.debug("Skips sending record command due to lacking id parameter");\r
229         return false;\r
230       }\r
231     }\r
232     return true;\r
233   }\r
234                                 \r
235   public String toggleRecord (String recId) {\r
236     if (hasRecord(recId)) {\r
237       pzreq.getRecord().removeParameters();  \r
238       pzresp.put("record", new RecordResponse());\r
239       return "";\r
240     } else {\r
241       pzreq.getRecord().setId(recId);\r
242       return doCommand("record");\r
243     }\r
244   }\r
245   \r
246   @Override\r
247   public boolean hasRecord (String recId) {\r
248     return pzreq.getCommand("record").hasParameters() && pzresp.getRecord().getRecId().equals(recId);\r
249   }\r
250         \r
251   public String getCurrentStateKey () {    \r
252     return stateMgr.getCurrentState().getKey();\r
253   }\r
254       \r
255   public void setCurrentStateKey(String key) {       \r
256     stateMgr.setCurrentStateKey(key);\r
257   }\r
258       \r
259   protected boolean hasQuery() {        \r
260     return pzreq.getCommand("search").hasParameterValue("query"); \r
261   }\r
262     \r
263     \r
264   @Override\r
265   public ResultsPager getPager () {\r
266     if (pager == null) {\r
267       pager = new ResultsPager(pzresp);      \r
268     } \r
269     return pager;      \r
270   }\r
271   \r
272   @Override\r
273   public ResultsPager setPager (int pageRange) {\r
274     pager =  new ResultsPager(pzresp,pageRange,pzreq);\r
275     return pager;\r
276   }\r
277     \r
278   protected void handleQueryStateChanges (String commands) {\r
279     if (stateMgr.hasPendingStateChange("search") && hasQuery()) { \r
280       logger.info("Triggered search: Found pending search change, doing search before updating " + commands);      \r
281       doSearch();\r
282     } \r
283     if (stateMgr.hasPendingStateChange("record") && ! commands.equals("record")) {        \r
284       logger.debug("Found pending record ID change. Doing record before updating " + commands);\r
285       stateMgr.hasPendingStateChange("record",false);\r
286       if (pzreq.getCommand("record").hasParameterValue("id")) {\r
287         update("record");\r
288       } else {         \r
289         pzresp.put("record", new RecordResponse());\r
290       }\r
291     }\r
292   }\r
293   \r
294   protected String doCommand(String commandName) {             \r
295     logger.debug(pzreq.getCommand(commandName).getEncodedQueryString() + ": Results for "+ pzreq.getCommand("search").getEncodedQueryString());\r
296     return update(commandName);\r
297   }\r
298   \r
299   @Override\r
300   public void stateUpdated(String commandName) {\r
301     logger.debug("State change reported for [" + commandName + "]");\r
302     if (commandName.equals("show")) {\r
303       logger.debug("Updating show");\r
304       update(commandName);\r
305     } \r
306   }\r
307   \r
308   public void setServiceProxyUrl(String url) {\r
309     searchClient = spClient;\r
310     setServiceType(SERVICE_TYPE_SP);\r
311     setServiceUrl(url);\r
312   }\r
313   \r
314   public String getServiceProxyUrl () {\r
315     if (isServiceProxyService()) {\r
316       return spClient.getServiceUrl();\r
317     } else {\r
318       return "";\r
319     }\r
320   }\r
321   \r
322   public void setPazpar2Url(String url) {\r
323     searchClient = pz2Client;\r
324     setServiceType(SERVICE_TYPE_PZ2);\r
325     setServiceUrl(url);\r
326   }\r
327   \r
328   public String getPazpar2Url() {\r
329     if (isPazpar2Service()) {\r
330       return pz2Client.getServiceUrl();\r
331     } else {\r
332       return "";\r
333     }\r
334   }\r
335 \r
336   \r
337   @Override\r
338   public void setServiceUrl(String url) {\r
339     if (url!=null && searchClient != null && !url.equals(searchClient.getServiceUrl())) {\r
340       pzreq.getRecord().removeParametersInState();\r
341       pzreq.getSearch().removeParametersInState();\r
342       pzresp.resetAllSessionData();\r
343       user.clear();\r
344       searchClient.setServiceUrl(url);\r
345     }    \r
346   }\r
347   \r
348   public String getServiceUrl() {\r
349     return (searchClient!=null ? searchClient.getServiceUrl() : "");\r
350   }\r
351   \r
352   public boolean getServiceUrlIsDefined() {\r
353     return (searchClient != null && searchClient.hasServiceUrl());\r
354   }\r
355   \r
356   public List<String> getServiceProxyUrls() {\r
357     List<String> urls = new ArrayList<String>();\r
358     urls.add("");\r
359     urls.addAll(serviceProxyUrls);\r
360     return urls;\r
361   }\r
362   \r
363   public List<String> getPazpar2Urls () {\r
364     List<String> urls = new ArrayList<String>();\r
365     urls.add("");\r
366     urls.addAll(pazpar2Urls);\r
367     return urls;\r
368   }\r
369   \r
370   public String getServiceType () {\r
371     return serviceType;\r
372   }\r
373   \r
374   public boolean isPazpar2Service () {\r
375     return serviceType.equals(SERVICE_TYPE_PZ2);\r
376   }\r
377   \r
378   public boolean isServiceProxyService() {\r
379     return serviceType.equals(SERVICE_TYPE_SP);\r
380   }\r
381   \r
382   public boolean serviceIsToBeDecided () {\r
383     return serviceType.equals(SERVICE_TYPE_TBD);\r
384   }\r
385   \r
386   public ServiceProxyClient getSpClient () {\r
387     return spClient;\r
388   }  \r
389   \r
390   @Override\r
391   public boolean getAuthenticationRequired () {\r
392     return spClient.isAuthenticatingClient();\r
393   }\r
394 \r
395   @Override\r
396   public String getCheckHistory () {\r
397     return ":pz2watch:stateForm:windowlocationhash";\r
398   }\r
399     \r
400   @Override\r
401   public String getWatchActiveclients () {\r
402     return ":pz2watch:activeclientsForm:activeclientsField";\r
403   }\r
404   \r
405   @Override\r
406   public String getWatchActiveclientsRecord () {\r
407     return ":pz2watch:activeclientsForm:activeclientsFieldRecord";\r
408   }\r
409 \r
410   @Override\r
411   public void configure(ConfigurationReader reader)\r
412       throws ConfigurationException {\r
413     Configuration config = reader.getConfiguration(this);\r
414     if (config == null) {\r
415       serviceType = SERVICE_TYPE_TBD;\r
416     } else {\r
417       String service = config.get("TYPE");\r
418       if (service == null || service.length()==0) {\r
419         serviceType = SERVICE_TYPE_TBD;\r
420       } else if (serviceTypes.contains(service.toUpperCase())) {        \r
421         setServiceType(service.toUpperCase());\r
422       } else {\r
423         logger.error("Unknown serviceType type in configuration [" + service + "], can be one of " + serviceTypes);\r
424         serviceType = SERVICE_TYPE_TBD;\r
425       }\r
426       serviceProxyUrls = config.getMultiProperty(SERVICE_PROXY_URL_LIST,",");\r
427       pazpar2Urls = config.getMultiProperty(PAZPAR2_URL_LIST, ",");\r
428     }\r
429     logger.info("Service Type is configured to " + serviceType);\r
430     \r
431   }\r
432 \r
433   @Override\r
434   public Map<String, String> getDefaults() {\r
435     return new HashMap<String,String>();\r
436   }\r
437 \r
438   @Override\r
439   public String getModuleName() {\r
440     return MODULE_NAME;\r
441   }\r
442 \r
443   @Override\r
444   public List<String> documentConfiguration() {\r
445     return new ArrayList<String>();\r
446   }\r
447 \r
448   @Override\r
449   public void setServiceTypePZ2() {\r
450     setServiceType(SERVICE_TYPE_PZ2);    \r
451   }\r
452 \r
453   @Override\r
454   public void setServiceTypeSP() {\r
455     setServiceType(SERVICE_TYPE_SP);        \r
456   }\r
457 \r
458   @Override\r
459   public void setServiceTypeTBD() {\r
460     setServiceType(SERVICE_TYPE_TBD);    \r
461   }\r
462   \r
463   private void setServiceType(String type) {\r
464     if (!serviceType.equals(type)  &&\r
465         !serviceType.equals(SERVICE_TYPE_TBD)) {\r
466       resetSearchAndRecordCommands();\r
467       pzresp.resetAllSessionData();\r
468     }\r
469     serviceType = type;\r
470     if (serviceType.equals(SERVICE_TYPE_PZ2)) {\r
471       searchClient = pz2Client;\r
472       logger.info("Setting a Pazpar2 client to serve requests.");\r
473     } else if (serviceType.equals(SERVICE_TYPE_SP)) {\r
474       searchClient = spClient;\r
475       logger.info("Setting a Service Proxy client to serve requests.");\r
476     } else {\r
477       logger.info("Clearing search client. No client defined to serve requests at this point.");\r
478       searchClient = null;\r
479     }\r
480   }\r
481   \r
482 }\r