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