Avoids npe in case of main config file missing
[mkjsf-moved-to-github.git] / src / main / java / com / indexdata / pz2utils4jsf / pazpar2 / Pz2Session.java
1 package com.indexdata.pz2utils4jsf.pazpar2;\r
2 \r
3 import java.io.IOException;\r
4 import java.util.ArrayList;\r
5 import java.util.List;\r
6 import java.util.Map;\r
7 import java.util.StringTokenizer;\r
8 import java.util.concurrent.ConcurrentHashMap;\r
9 \r
10 import javax.enterprise.context.SessionScoped;\r
11 import javax.inject.Named;\r
12 \r
13 import org.apache.log4j.Logger;\r
14 \r
15 import com.indexdata.masterkey.pazpar2.client.Pazpar2Client;\r
16 import com.indexdata.masterkey.pazpar2.client.Pazpar2ClientConfiguration;\r
17 import com.indexdata.masterkey.pazpar2.client.Pazpar2ClientGeneric;\r
18 import com.indexdata.masterkey.pazpar2.client.exceptions.ProxyErrorException;\r
19 import com.indexdata.pz2utils4jsf.config.Pz2Configurator;\r
20 import com.indexdata.pz2utils4jsf.controls.ResultsPager;\r
21 import com.indexdata.pz2utils4jsf.errors.ApplicationError;\r
22 import com.indexdata.pz2utils4jsf.errors.ErrorHelper;\r
23 import com.indexdata.pz2utils4jsf.errors.ConfigurationError;\r
24 import com.indexdata.pz2utils4jsf.pazpar2.data.CommandError;\r
25 import com.indexdata.pz2utils4jsf.pazpar2.data.ByTarget;\r
26 import com.indexdata.pz2utils4jsf.pazpar2.data.Pazpar2ResponseData;\r
27 import com.indexdata.pz2utils4jsf.pazpar2.data.Pazpar2ResponseParser;\r
28 import com.indexdata.pz2utils4jsf.pazpar2.data.RecordResponse;\r
29 import com.indexdata.pz2utils4jsf.pazpar2.data.SearchResponse;\r
30 import com.indexdata.pz2utils4jsf.pazpar2.data.ShowResponse;\r
31 import com.indexdata.pz2utils4jsf.pazpar2.data.StatResponse;\r
32 import com.indexdata.pz2utils4jsf.pazpar2.data.TermListsResponse;\r
33 import com.indexdata.pz2utils4jsf.pazpar2.data.TermResponse;\r
34 import com.indexdata.pz2utils4jsf.pazpar2.state.QueryStates;\r
35 import com.indexdata.pz2utils4jsf.utils.Utils;\r
36 \r
37 @Named @SessionScoped  \r
38 public class Pz2Session implements Pz2Interface {\r
39   \r
40   private static Logger logger = Logger.getLogger(Pz2Session.class);\r
41   \r
42   private Map<String,Pazpar2ResponseData> dataObjects = new ConcurrentHashMap<String,Pazpar2ResponseData>();\r
43   private QueryStates queryStates = new QueryStates();\r
44   \r
45   private static final long serialVersionUID = 3947514708343320514L;  \r
46   private Pazpar2ClientConfiguration cfg = null;\r
47   private Pazpar2Client client = null;   \r
48   private TargetFilter targetFilter = null;  \r
49   private ResultsPager pager = null; \r
50   private ErrorHelper errorHelper = null;\r
51   private List<ApplicationError> configurationErrors = null;\r
52   \r
53   public Pz2Session () {\r
54     logger.info("Instantiating pz2 session object [" + Utils.objectId(this) + "]");      \r
55   }\r
56     \r
57   public void init(Pz2Configurator pz2conf) {\r
58     if (client==null) {\r
59       configurationErrors = new ArrayList<ApplicationError>();\r
60       errorHelper = new ErrorHelper(pz2conf);\r
61       logger.info(Utils.objectId(this) + " is configuring itself using the provided " + Utils.objectId(pz2conf));\r
62       try {\r
63         cfg = new Pazpar2ClientConfiguration(pz2conf.getConfig());\r
64       } catch (ProxyErrorException pe) {\r
65         logger.error("Could not configure Pazpar2 client: " + pe.getMessage());\r
66         configurationErrors.add(new ConfigurationError("Pz2Client Config","ProxyError","Could not configure Pazpar2 client: " + pe.getMessage(),errorHelper));\r
67       } catch (IOException io) {\r
68         logger.error("Could not configure Pazpar2 client: " + io.getMessage());\r
69         configurationErrors.add(new ConfigurationError("Pz2Client Config","ProxyError","Could not configure Pazpar2 client: " + io.getMessage(),errorHelper));\r
70       }\r
71       if (cfg != null) {\r
72         try {\r
73           client = new Pazpar2ClientGeneric(cfg);     \r
74         } catch (ProxyErrorException pe) {\r
75           logger.error("Could not instantiate Pazpar2 client: " + pe.getMessage());\r
76           configurationErrors.add(new ConfigurationError("Pz2Client error","ProxyError","Could not create Pazpar2 client: " +pe.getMessage(),errorHelper));                \r
77         } \r
78         logger.info("Got " + configurationErrors.size() + " configuration errors");        \r
79       }\r
80       resetDataObjects();\r
81     } else {\r
82       logger.warn("Attempt to configure session but it already has a configured client");\r
83     }\r
84   }\r
85     \r
86   public void doSearch(String query) {\r
87     setCommandParameter("search",new CommandParameter("query","=",query));     \r
88     doSearch();\r
89   }\r
90 \r
91   public void doSearch() { \r
92     queryStates.hasPendingStateChange("search",false);\r
93     resetDataObjects();\r
94     setCommandParameter("show",new CommandParameter("start","=",0));    \r
95     logger.debug(Utils.objectId(this) + " is searching using "+getCommand("search").getParameter("query").getEncodedQueryString());\r
96     doCommand("search");    \r
97   }\r
98       \r
99   /**\r
100    * Refreshes 'show', 'stat', 'termlist', and 'bytarget' data object from pazpar2\r
101    * \r
102    * @return Number of activeclients at the time of the 'show' command.\r
103    */\r
104   public String update () {\r
105     logger.debug("Updating show,stat,termlist,bytarget from pazpar2");\r
106     return update("show,stat,termlist,bytarget");\r
107   }\r
108    \r
109   /**\r
110    * Refreshes the data objects listed in 'commands' from pazpar2\r
111    * \r
112    * @param commands\r
113    * @return Number of activeclients at the time of the 'show' command\r
114    */\r
115   public String update (String commands) {\r
116     if (! hasConfigurationErrors()) {\r
117       if (hasQuery()) {\r
118         handleQueryStateChanges(commands);\r
119         logger.debug("Processing request for " + commands); \r
120         List<CommandThread> threadList = new ArrayList<CommandThread>();\r
121         StringTokenizer tokens = new StringTokenizer(commands,",");\r
122         while (tokens.hasMoreElements()) {\r
123           threadList.add(new CommandThread(getCommand(tokens.nextToken()),client));            \r
124         }\r
125         for (CommandThread thread : threadList) {\r
126           thread.start();\r
127         }\r
128         for (CommandThread thread : threadList) {\r
129           try {\r
130             thread.join();\r
131           } catch (InterruptedException e) {\r
132             e.printStackTrace();\r
133           }\r
134         }\r
135         for (CommandThread thread : threadList) {\r
136            String commandName = thread.getCommand().getName();\r
137            Pazpar2ResponseData responseObject = Pazpar2ResponseParser.getParser().getDataObject(thread.getResponse());\r
138            dataObjects.put(commandName, responseObject);        \r
139         }\r
140         return getActiveClients();\r
141       } else {\r
142         logger.debug("Skipped requests for " + commands + " as there's not yet a query."); \r
143         resetDataObjects();\r
144         return "0";\r
145       }\r
146     } else {\r
147       configurationErrors.add(\r
148           new ConfigurationError("Querying while errors",\r
149                                  "App halted",\r
150                                  "Cannot query Pazpar2 while there are configuration errors.",\r
151                                  errorHelper));\r
152       return "0";\r
153     }\r
154     \r
155   }\r
156         \r
157   public void setQuery (String query) {\r
158     logger.debug("Creating new command parameter for " + query);\r
159     setCommandParameter("search",new CommandParameter("query","=",query));\r
160   }\r
161   \r
162   public String getQuery () {\r
163     return getCommandParameterValueSimple("search","query",null);\r
164   }\r
165   \r
166   public void setFacet (String facetKey, String term) {           \r
167     if (term != null && term.length()>0) {\r
168       queryStates.getCurrentState().setCommandParameterExpression("search","query",new Expression(facetKey,"=",term),queryStates);\r
169       doSearch();\r
170     }            \r
171   }\r
172   \r
173   public void setFacetOnQuery (String facetKey, String term) {\r
174     String facetExpression = facetKey + "=" + term;    \r
175     if (term != null && term.length()>0) {\r
176       setCommandParameter("search",new CommandParameter("query","=", getQuery() + " and " + facetExpression));\r
177       doSearch();        \r
178     }            \r
179   }\r
180       \r
181   public void removeFacet(String facetKey, String term) {\r
182     queryStates.getCurrentState().removeCommandParameterExpression("search","query",new Expression(facetKey,"=",term),queryStates);\r
183     doSearch();\r
184   }\r
185   \r
186   public void setTargetFilter (String targetId, String targetName) {    \r
187     if (hasTargetFilter(new TargetFilter(targetId,targetName))) {\r
188       logger.debug("Already using target filter " + this.targetFilter.getFilterExpression());\r
189     } else {      \r
190       this.targetFilter = new TargetFilter(targetId,targetName);\r
191       setCommandParameter("search",new CommandParameter("filter","=",this.targetFilter.getFilterExpression()));      \r
192       doSearch();\r
193     }    \r
194   }\r
195 \r
196   public TargetFilter getTargetFilter () {\r
197     return targetFilter;\r
198   }\r
199     \r
200   public void removeTargetFilter () {\r
201     logger.debug("Removing target filter " + targetFilter.getFilterExpression());\r
202     this.targetFilter = null;\r
203     removeCommandParameter("search","filter");         \r
204     doSearch();\r
205   }\r
206   \r
207   public boolean hasTargetFilter() {\r
208     return targetFilter != null;    \r
209   }\r
210         \r
211   public void setSort (String sortOption) {\r
212     logger.debug("Setting sort option: " + sortOption);\r
213     setCommandParameter("show",new CommandParameter("sort","=",sortOption));\r
214     update("show");\r
215   }\r
216   \r
217   public String getSort () {\r
218     return getCommandParameterValue("show","sort","relevance");\r
219   }\r
220     \r
221   public void setPageSize (int perPageOption) {\r
222     if (getPageSize()!=perPageOption) {\r
223      logger.debug("Setting perpage option to " + perPageOption + " and resetting start page.");\r
224      setCommandParameter("show",new CommandParameter("num","=",perPageOption));\r
225      setCommandParameter("show",new CommandParameter("start","=",0));\r
226      update("show");\r
227     } else {\r
228       logger.debug("Not updating page size, already is " + perPageOption);\r
229     }\r
230   }\r
231   \r
232   public int getPageSize () {\r
233     return getCommandParameterValue("show","num",20);\r
234   }\r
235   \r
236   public void setStart (int start) {\r
237     logger.debug("Setting start num to " + start);\r
238     setCommandParameter("show", new CommandParameter("start","=",start));  \r
239     update("show");\r
240   }\r
241   \r
242   public int getStart() {\r
243     return getCommandParameterValue("show","start",0);\r
244   }\r
245         \r
246   public String toggleRecord (String recId) {\r
247     if (hasRecord(recId)) {\r
248       removeCommand("record");  \r
249       dataObjects.put("record", new RecordResponse());\r
250       return "";\r
251     } else {\r
252       return updateRecord(recId);\r
253     }\r
254   }\r
255   \r
256   private String updateRecord(String recId) {    \r
257     setCommandParameter("record",new CommandParameter("id","=",recId));    \r
258     return doCommand("record");    \r
259   }\r
260   \r
261   public boolean hasRecord (String recId) {\r
262     return getCommand("record").hasParameters() && getRecord().getRecId().equals(recId);\r
263   }\r
264       \r
265   public ShowResponse getShow () {\r
266     return ((ShowResponse) dataObjects.get("show"));\r
267   }\r
268   \r
269   public StatResponse getStat () {\r
270     return ((StatResponse) dataObjects.get("stat"));\r
271   }\r
272   \r
273   public RecordResponse getRecord() {\r
274     return ((RecordResponse) dataObjects.get("record"));\r
275   }\r
276   \r
277   public TermListsResponse getTermLists () {\r
278     return ((TermListsResponse) dataObjects.get("termlist"));\r
279   }\r
280   \r
281   public List<TermResponse> getFacetTerms (String facet, int count) {\r
282     return (getTermLists().getTermList(facet).getTerms(count));\r
283   }\r
284     \r
285   public List<TermResponse> getFacetTerms (String facet) {\r
286     return (getTermLists().getTermList(facet).getTerms());\r
287   }\r
288   \r
289   public ByTarget getByTarget() {\r
290     return ((ByTarget) dataObjects.get("bytarget"));\r
291   }\r
292   \r
293   \r
294   public String getCurrentStateKey () {    \r
295     return queryStates.getCurrentStateKey();\r
296   }\r
297       \r
298   public void setCurrentStateKey(String key) {       \r
299     queryStates.setCurrentStateKey(key);\r
300   }\r
301   \r
302   public boolean hasConfigurationErrors () {\r
303       return (configurationErrors.size()>0);      \r
304   }\r
305   \r
306   public boolean hasCommandErrors () {\r
307     if (dataObjects.get("search").hasApplicationError()) {\r
308       logger.info("Error detected in search");\r
309       return true;\r
310     }\r
311     for (String name : dataObjects.keySet()) {\r
312       if (dataObjects.get(name).hasApplicationError()) {\r
313         logger.info("Error detected in " + name);\r
314         return true;\r
315       }\r
316     }    \r
317     return false;    \r
318   }\r
319   \r
320   /**\r
321    * Returns true if application error found in any response data objects \r
322    */\r
323   public boolean hasErrors () {\r
324     return hasConfigurationErrors() || hasCommandErrors();\r
325   }\r
326 \r
327   public List<ApplicationError> getConfigurationErrors() {    \r
328     return configurationErrors;\r
329   }\r
330   \r
331   /**\r
332    * Returns a search command error, if any, otherwise the first\r
333    * error found for an arbitrary command, if any, otherwise\r
334    * an empty dummy error. \r
335    */    \r
336   public ApplicationError getCommandError() {\r
337     CommandError error = new CommandError();    \r
338     if (dataObjects.get("search").hasApplicationError()) {\r
339       error = dataObjects.get("search").getApplicationError();                        \r
340     } else {\r
341       for (String name : dataObjects.keySet()) {     \r
342         if (dataObjects.get(name).hasApplicationError()) {     \r
343           error = dataObjects.get(name).getApplicationError(); \r
344           break;\r
345         } \r
346       }\r
347     }\r
348     error.setErrorHelper(errorHelper);\r
349     return error;         \r
350   }\r
351 \r
352     \r
353   private boolean hasTargetFilter(TargetFilter targetFilter) {\r
354     return hasTargetFilter() && targetFilter.equals(this.targetFilter);\r
355   }\r
356   \r
357   private boolean hasQuery() {\r
358     return !(getCommand("search").getParameter("query") == null);\r
359   }\r
360     \r
361   public boolean hasRecords () {\r
362     return getStat().getRecords() > 0            \r
363            && getShow().getHits() != null \r
364            && getShow().getHits().size()>0;\r
365   }\r
366     \r
367   public ResultsPager getPager () {\r
368     if (pager == null) {\r
369       pager = new ResultsPager(this);      \r
370     } \r
371     return pager;      \r
372   }\r
373   \r
374   public ResultsPager setPager (int pageRange) {\r
375     pager =  new ResultsPager(this,pageRange);\r
376     return pager;\r
377   }\r
378   \r
379   protected ErrorHelper getTroubleshooter() {\r
380     return errorHelper;\r
381   }\r
382   \r
383   private void handleQueryStateChanges (String commands) {\r
384     if (queryStates.hasPendingStateChange("search")) { \r
385       logger.debug("Found pending search change. Doing search before updating " + commands);\r
386       doSearch();\r
387     } \r
388     if (queryStates.hasPendingStateChange("record") && ! commands.equals("record")) {        \r
389       logger.debug("Found pending record ID change. Doing record before updating " + commands);\r
390       queryStates.hasPendingStateChange("record",false);\r
391       if (getCommand("record").hasParameters()) {\r
392         updateRecord(getCommand("record").getParameter("id").getSimpleValue());\r
393       } else {\r
394         removeCommand("record");  \r
395         dataObjects.put("record", new RecordResponse());\r
396       }\r
397     }    \r
398   }\r
399 \r
400   private String getActiveClients() {    \r
401     if (getShow()!=null) {\r
402       logger.debug("Active clients: "+getShow().getActiveClients());\r
403       return getShow().getActiveClients();\r
404     } else {\r
405       return "";\r
406     }\r
407   }\r
408 \r
409   private Pazpar2Command getCommand(String name) {\r
410     return queryStates.getCurrentState().getCommand(name);\r
411   }\r
412   \r
413   private void setCommandParameter(String commandName, CommandParameter parameter) {\r
414     logger.debug("Setting parameter for " + commandName + ": " + parameter);\r
415     queryStates.getCurrentState().setCommandParameter(commandName, parameter, queryStates);    \r
416   }\r
417   \r
418   \r
419   private void removeCommandParameter(String commandName, String parameterName) {\r
420     queryStates.getCurrentState().removeCommandParameter(commandName,parameterName,queryStates);    \r
421   }\r
422   \r
423   private void removeCommand (String commandName) {\r
424     queryStates.getCurrentState().removeCommand(commandName, queryStates);\r
425   }\r
426     \r
427   private String getCommandParameterValue (String commandName, String parameterName, String defaultValue) {    \r
428     Pazpar2Command command = getCommand(commandName);\r
429     if (command != null) {\r
430       CommandParameter parameter = command.getParameter(parameterName);\r
431       if (parameter != null) {\r
432         return parameter.getValueWithExpressions();\r
433       }\r
434     }\r
435     return defaultValue;    \r
436   }\r
437   \r
438   private String getCommandParameterValueSimple (String commandName, String parameterName, String defaultValue) {    \r
439     Pazpar2Command command = getCommand(commandName);\r
440     if (command != null) {\r
441       CommandParameter parameter = command.getParameter(parameterName);\r
442       if (parameter != null) {\r
443         return parameter.getSimpleValue();\r
444       }\r
445     }\r
446     return defaultValue;    \r
447   }\r
448 \r
449   \r
450   private int getCommandParameterValue (String commandName, String parameterName, int defaultValue) {\r
451     Pazpar2Command command = getCommand(commandName);\r
452     if (command != null) {\r
453       CommandParameter parameter = command.getParameter(parameterName);\r
454       if (parameter != null) {\r
455         return Integer.parseInt(parameter.getSimpleValue());\r
456       }\r
457     }\r
458     return defaultValue;    \r
459   }\r
460 \r
461   private String doCommand(String commandName) {      \r
462     Pazpar2Command command = getCommand(commandName);    \r
463     logger.debug(command.getEncodedQueryString() + ": Results for "+ getCommand("search").getEncodedQueryString());\r
464     return update(commandName);      \r
465   }\r
466   \r
467   private void resetDataObjects() {\r
468     logger.debug("Resetting show,stat,termlist,bytarget,search response objects.");\r
469     dataObjects = new ConcurrentHashMap<String,Pazpar2ResponseData>();\r
470     dataObjects.put("show", new ShowResponse());\r
471     dataObjects.put("stat", new StatResponse());\r
472     dataObjects.put("termlist", new TermListsResponse());\r
473     dataObjects.put("bytarget", new ByTarget());\r
474     dataObjects.put("record", new RecordResponse());\r
475     dataObjects.put("search", new SearchResponse());\r
476   }\r
477   \r
478 }\r