29403070bad23418443ec970dd3ea9454cf60a4a
[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       try {\r
72         client = new Pazpar2ClientGeneric(cfg);     \r
73       } catch (ProxyErrorException pe) {\r
74         logger.error("Could not instantiate Pazpar2 client: " + pe.getMessage());\r
75         configurationErrors.add(new ConfigurationError("Pz2Client error","ProxyError","Could not create Pazpar2 client: " +pe.getMessage(),errorHelper));                \r
76       } \r
77       logger.info("Got " + configurationErrors.size() + " configuration errors");\r
78       resetDataObjects();\r
79     } else {\r
80       logger.warn("Attempt to configure session but it already has a configured client");\r
81     }\r
82   }\r
83     \r
84   public void doSearch(String query) {\r
85     setCommandParameter("search",new CommandParameter("query","=",query));     \r
86     doSearch();\r
87   }\r
88 \r
89   public void doSearch() { \r
90     queryStates.hasPendingStateChange("search",false);\r
91     resetDataObjects();\r
92     setCommandParameter("show",new CommandParameter("start","=",0));    \r
93     logger.debug(Utils.objectId(this) + " is searching using "+getCommand("search").getParameter("query").getEncodedQueryString());\r
94     doCommand("search");    \r
95   }\r
96       \r
97   /**\r
98    * Refreshes 'show', 'stat', 'termlist', and 'bytarget' data object from pazpar2\r
99    * \r
100    * @return Number of activeclients at the time of the 'show' command.\r
101    */\r
102   public String update () {\r
103     logger.debug("Updating show,stat,termlist,bytarget from pazpar2");\r
104     return update("show,stat,termlist,bytarget");\r
105   }\r
106    \r
107   /**\r
108    * Refreshes the data objects listed in 'commands' from pazpar2\r
109    * \r
110    * @param commands\r
111    * @return Number of activeclients at the time of the 'show' command\r
112    */\r
113   public String update (String commands) {\r
114     if (! hasConfigurationErrors()) {\r
115       if (hasQuery()) {\r
116         handleQueryStateChanges(commands);\r
117         logger.debug("Processing request for " + commands); \r
118         List<CommandThread> threadList = new ArrayList<CommandThread>();\r
119         StringTokenizer tokens = new StringTokenizer(commands,",");\r
120         while (tokens.hasMoreElements()) {\r
121           threadList.add(new CommandThread(getCommand(tokens.nextToken()),client));            \r
122         }\r
123         for (CommandThread thread : threadList) {\r
124           thread.start();\r
125         }\r
126         for (CommandThread thread : threadList) {\r
127           try {\r
128             thread.join();\r
129           } catch (InterruptedException e) {\r
130             e.printStackTrace();\r
131           }\r
132         }\r
133         for (CommandThread thread : threadList) {\r
134            String commandName = thread.getCommand().getName();\r
135            Pazpar2ResponseData responseObject = Pazpar2ResponseParser.getParser().getDataObject(thread.getResponse());\r
136            dataObjects.put(commandName, responseObject);        \r
137         }\r
138         return getActiveClients();\r
139       } else {\r
140         logger.debug("Skipped requests for " + commands + " as there's not yet a query."); \r
141         resetDataObjects();\r
142         return "0";\r
143       }\r
144     } else {\r
145       configurationErrors.add(\r
146           new ConfigurationError("Querying while errors",\r
147                                  "App halted",\r
148                                  "Cannot query Pazpar2 while there are configuration errors.",\r
149                                  errorHelper));\r
150       return "0";\r
151     }\r
152     \r
153   }\r
154         \r
155   public void setQuery (String query) {\r
156     logger.debug("Creating new command parameter for " + query);\r
157     setCommandParameter("search",new CommandParameter("query","=",query));\r
158   }\r
159   \r
160   public String getQuery () {\r
161     return getCommandParameterValueSimple("search","query",null);\r
162   }\r
163   \r
164   public void setFacet (String facetKey, String term) {           \r
165     if (term != null && term.length()>0) {\r
166       queryStates.getCurrentState().setCommandParameterExpression("search","query",new Expression(facetKey,"=",term),queryStates);\r
167       doSearch();\r
168     }            \r
169   }\r
170   \r
171   public void setFacetOnQuery (String facetKey, String term) {\r
172     String facetExpression = facetKey + "=" + term;    \r
173     if (term != null && term.length()>0) {\r
174       setCommandParameter("search",new CommandParameter("query","=", getQuery() + " and " + facetExpression));\r
175       doSearch();        \r
176     }            \r
177   }\r
178       \r
179   public void removeFacet(String facetKey, String term) {\r
180     queryStates.getCurrentState().removeCommandParameterExpression("search","query",new Expression(facetKey,"=",term),queryStates);\r
181     doSearch();\r
182   }\r
183   \r
184   public void setTargetFilter (String targetId, String targetName) {    \r
185     if (hasTargetFilter(new TargetFilter(targetId,targetName))) {\r
186       logger.debug("Already using target filter " + this.targetFilter.getFilterExpression());\r
187     } else {      \r
188       this.targetFilter = new TargetFilter(targetId,targetName);\r
189       setCommandParameter("search",new CommandParameter("filter","=",this.targetFilter.getFilterExpression()));      \r
190       doSearch();\r
191     }    \r
192   }\r
193 \r
194   public TargetFilter getTargetFilter () {\r
195     return targetFilter;\r
196   }\r
197     \r
198   public void removeTargetFilter () {\r
199     logger.debug("Removing target filter " + targetFilter.getFilterExpression());\r
200     this.targetFilter = null;\r
201     removeCommandParameter("search","filter");         \r
202     doSearch();\r
203   }\r
204   \r
205   public boolean hasTargetFilter() {\r
206     return targetFilter != null;    \r
207   }\r
208         \r
209   public void setSort (String sortOption) {\r
210     logger.debug("Setting sort option: " + sortOption);\r
211     setCommandParameter("show",new CommandParameter("sort","=",sortOption));\r
212     update("show");\r
213   }\r
214   \r
215   public String getSort () {\r
216     return getCommandParameterValue("show","sort","relevance");\r
217   }\r
218     \r
219   public void setPageSize (int perPageOption) {\r
220     if (getPageSize()!=perPageOption) {\r
221      logger.debug("Setting perpage option to " + perPageOption + " and resetting start page.");\r
222      setCommandParameter("show",new CommandParameter("num","=",perPageOption));\r
223      setCommandParameter("show",new CommandParameter("start","=",0));\r
224      update("show");\r
225     } else {\r
226       logger.debug("Not updating page size, already is " + perPageOption);\r
227     }\r
228   }\r
229   \r
230   public int getPageSize () {\r
231     return getCommandParameterValue("show","num",20);\r
232   }\r
233   \r
234   public void setStart (int start) {\r
235     logger.debug("Setting start num to " + start);\r
236     setCommandParameter("show", new CommandParameter("start","=",start));  \r
237     update("show");\r
238   }\r
239   \r
240   public int getStart() {\r
241     return getCommandParameterValue("show","start",0);\r
242   }\r
243         \r
244   public String toggleRecord (String recId) {\r
245     if (hasRecord(recId)) {\r
246       removeCommand("record");  \r
247       dataObjects.put("record", new RecordResponse());\r
248       return "";\r
249     } else {\r
250       return updateRecord(recId);\r
251     }\r
252   }\r
253   \r
254   private String updateRecord(String recId) {    \r
255     setCommandParameter("record",new CommandParameter("id","=",recId));    \r
256     return doCommand("record");    \r
257   }\r
258   \r
259   public boolean hasRecord (String recId) {\r
260     return getCommand("record").hasParameters() && getRecord().getRecId().equals(recId);\r
261   }\r
262       \r
263   public ShowResponse getShow () {\r
264     return ((ShowResponse) dataObjects.get("show"));\r
265   }\r
266   \r
267   public StatResponse getStat () {\r
268     return ((StatResponse) dataObjects.get("stat"));\r
269   }\r
270   \r
271   public RecordResponse getRecord() {\r
272     return ((RecordResponse) dataObjects.get("record"));\r
273   }\r
274   \r
275   public TermListsResponse getTermLists () {\r
276     return ((TermListsResponse) dataObjects.get("termlist"));\r
277   }\r
278   \r
279   public List<TermResponse> getFacetTerms (String facet, int count) {\r
280     return (getTermLists().getTermList(facet).getTerms(count));\r
281   }\r
282     \r
283   public List<TermResponse> getFacetTerms (String facet) {\r
284     return (getTermLists().getTermList(facet).getTerms());\r
285   }\r
286   \r
287   public ByTarget getByTarget() {\r
288     return ((ByTarget) dataObjects.get("bytarget"));\r
289   }\r
290   \r
291   \r
292   public String getCurrentStateKey () {    \r
293     return queryStates.getCurrentStateKey();\r
294   }\r
295       \r
296   public void setCurrentStateKey(String key) {\r
297     logger.debug("************** request to set state key to: [" + key + "]");    \r
298     queryStates.setCurrentStateKey(key);\r
299   }\r
300   \r
301   public boolean hasConfigurationErrors () {\r
302       return (configurationErrors.size()>0);      \r
303   }\r
304   \r
305   public boolean hasCommandErrors () {\r
306     if (dataObjects.get("search").hasApplicationError()) {\r
307       logger.info("Error detected in search");\r
308       return true;\r
309     }\r
310     for (String name : dataObjects.keySet()) {\r
311       if (dataObjects.get(name).hasApplicationError()) {\r
312         logger.info("Error detected in " + name);\r
313         return true;\r
314       }\r
315     }    \r
316     return false;    \r
317   }\r
318   \r
319   /**\r
320    * Returns true if application error found in any response data objects \r
321    */\r
322   public boolean hasErrors () {\r
323     return hasConfigurationErrors() || hasCommandErrors();\r
324   }\r
325 \r
326   public List<ApplicationError> getConfigurationErrors() {\r
327     logger.info("Returning " + configurationErrors.size() + " configuration errors");\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
479 }\r