48aa23652127ec2aa062fde8e60d4a229f119bcd
[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.util.ArrayList;\r
4 import java.util.List;\r
5 import java.util.Map;\r
6 import java.util.StringTokenizer;\r
7 import java.util.concurrent.ConcurrentHashMap;\r
8 \r
9 import javax.enterprise.context.SessionScoped;\r
10 import javax.inject.Named;\r
11 \r
12 import org.apache.log4j.Logger;\r
13 \r
14 import com.indexdata.pz2utils4jsf.config.ConfigurationReader;\r
15 import com.indexdata.pz2utils4jsf.controls.ResultsPager;\r
16 import com.indexdata.pz2utils4jsf.errors.ConfigurationError;\r
17 import com.indexdata.pz2utils4jsf.errors.ConfigurationException;\r
18 import com.indexdata.pz2utils4jsf.errors.ErrorHelper;\r
19 import com.indexdata.pz2utils4jsf.errors.ErrorInterface;\r
20 import com.indexdata.pz2utils4jsf.pazpar2.data.ByTarget;\r
21 import com.indexdata.pz2utils4jsf.pazpar2.data.CommandError;\r
22 import com.indexdata.pz2utils4jsf.pazpar2.data.Pazpar2ResponseData;\r
23 import com.indexdata.pz2utils4jsf.pazpar2.data.Pazpar2ResponseParser;\r
24 import com.indexdata.pz2utils4jsf.pazpar2.data.RecordResponse;\r
25 import com.indexdata.pz2utils4jsf.pazpar2.data.SearchResponse;\r
26 import com.indexdata.pz2utils4jsf.pazpar2.data.ShowResponse;\r
27 import com.indexdata.pz2utils4jsf.pazpar2.data.StatResponse;\r
28 import com.indexdata.pz2utils4jsf.pazpar2.data.TermListsResponse;\r
29 import com.indexdata.pz2utils4jsf.pazpar2.data.TermResponse;\r
30 import com.indexdata.pz2utils4jsf.pazpar2.state.StateManager;\r
31 import com.indexdata.pz2utils4jsf.utils.Utils;\r
32 \r
33 @Named @SessionScoped  \r
34 public class Pz2Session implements Pz2Interface {\r
35     \r
36   private static final long serialVersionUID = 3947514708343320514L;\r
37   private static Logger logger = Logger.getLogger(Pz2Session.class);\r
38   \r
39   protected Map<String,Pazpar2ResponseData> dataObjects = new ConcurrentHashMap<String,Pazpar2ResponseData>();\r
40   protected StateManager stateManager = new StateManager();\r
41   protected ErrorHelper errorHelper = null;\r
42   \r
43   protected List<ErrorInterface> configurationErrors = null;\r
44   protected SearchClient searchClient = null;   \r
45   protected SingleTargetFilter singleTargetFilter = null;  \r
46   protected ResultsPager pager = null; \r
47     \r
48   public Pz2Session () {\r
49     logger.info("Instantiating pz2 session object [" + Utils.objectId(this) + "]");      \r
50   }\r
51     \r
52   public void configureClient(SearchClient searchClient, ConfigurationReader configReader) {\r
53     configurationErrors = new ArrayList<ErrorInterface>();\r
54     errorHelper = new ErrorHelper(configReader);    \r
55     logger.debug(Utils.objectId(this) + " will configure search client for the session");\r
56     try {\r
57       searchClient.configure(configReader);            \r
58       // At the time of writing this search client is injected using Weld. \r
59       // However, the client is used for asynchronously sending off requests\r
60       // to the server AND propagation of context to threads is currently \r
61       // not supported. Trying to do so throws a WELD-001303 error. \r
62       // To avoid that, a context free client is cloned from the context \r
63       // dependent one. \r
64       // If propagation to threads gets supported, the cloning can go. \r
65       this.searchClient = searchClient.cloneMe();         \r
66     } catch (ConfigurationException e) {\r
67       configurationErrors.add(new ConfigurationError("Search Client","Configuration",e.getMessage(),new ErrorHelper(configReader)));          \r
68     } \r
69     logger.info(configReader.document());\r
70     resetDataObjects();\r
71   }\r
72       \r
73   public void doSearch(String query) {\r
74     setCommandParameter("search",new CommandParameter("query","=",query));     \r
75     doSearch();\r
76   }\r
77 \r
78   public void doSearch() { \r
79     stateManager.hasPendingStateChange("search",false);\r
80     resetDataObjects();\r
81     removeCommand("record");\r
82     setCommandParameter("show",new CommandParameter("start","=",0));    \r
83     logger.debug(Utils.objectId(this) + " is searching using "+getCommand("search").getParameter("query").getEncodedQueryString());\r
84     doCommand("search");    \r
85   }\r
86       \r
87   /**\r
88    * Refreshes 'show', 'stat', 'termlist', and 'bytarget' data object from pazpar2\r
89    * \r
90    * @return Number of activeclients at the time of the 'show' command.\r
91    */\r
92   public String update () {\r
93     logger.debug("Updating show,stat,termlist,bytarget from pazpar2");\r
94     return update("show,stat,termlist,bytarget");\r
95   }\r
96    \r
97   /**\r
98    * Refreshes the data objects listed in 'commands' from pazpar2\r
99    * \r
100    * @param commands\r
101    * @return Number of activeclients at the time of the 'show' command\r
102    */\r
103   public String update (String commands) {\r
104     if (! hasConfigurationErrors()) {\r
105       if (hasQuery()) {\r
106         handleQueryStateChanges(commands);\r
107         logger.debug("Processing request for " + commands); \r
108         List<CommandThread> threadList = new ArrayList<CommandThread>();\r
109         StringTokenizer tokens = new StringTokenizer(commands,",");\r
110         while (tokens.hasMoreElements()) {          \r
111           threadList.add(new CommandThread(getCommand(tokens.nextToken()),searchClient));            \r
112         }\r
113         for (CommandThread thread : threadList) {\r
114           thread.start();\r
115         }\r
116         for (CommandThread thread : threadList) {\r
117           try {\r
118             thread.join();\r
119           } catch (InterruptedException e) {\r
120             e.printStackTrace();\r
121           }\r
122         }\r
123         for (CommandThread thread : threadList) {\r
124            String commandName = thread.getCommand().getName();\r
125            String response = thread.getResponse();\r
126            logger.debug("Response was: " + response);\r
127            Pazpar2ResponseData responseObject = Pazpar2ResponseParser.getParser().getDataObject(response);\r
128            dataObjects.put(commandName, responseObject);        \r
129         }\r
130         if (commands.equals("record")) {\r
131           logger.debug("Record: Active clients: "+getRecord().getActiveClients());\r
132           return getRecord().getActiveClients();\r
133         } else {\r
134           return getActiveClients();\r
135         }  \r
136       } else {\r
137         logger.debug("Skipped requests for " + commands + " as there's not yet a query."); \r
138         resetDataObjects();\r
139         return "0";\r
140       }\r
141     } else {\r
142       logger.error("Did not attempt to execute query since there are configuration errors.");\r
143       return "0";\r
144     }\r
145     \r
146   }\r
147         \r
148   public void setQuery (String query) {\r
149     logger.debug("Creating new command parameter for " + query);\r
150     setCommandParameter("search",new CommandParameter("query","=",query));\r
151   }\r
152   \r
153   public String getQuery () {\r
154     return getCommandParameterValueSimple("search","query",null);\r
155   }\r
156   \r
157   public void setFacet (String facetKey, String term) {           \r
158     if (term != null && term.length()>0) {   \r
159       Pazpar2Command command = getCommand("search");\r
160       command.getParameter("query").addExpression(new Expression(facetKey,"=",term));\r
161       stateManager.checkIn(command);\r
162       doSearch();\r
163     }            \r
164   }\r
165   \r
166   public void setFacetOnQuery (String facetKey, String term) {\r
167     String facetExpression = facetKey + "=" + term;    \r
168     if (term != null && term.length()>0) {\r
169       setCommandParameter("search",new CommandParameter("query","=", getQuery() + " and " + facetExpression));\r
170       doSearch();        \r
171     }            \r
172   }\r
173       \r
174   public void removeFacet(String facetKey, String term) {\r
175     Pazpar2Command command = getCommand("search");\r
176     command.getParameter("query").removeExpression(new Expression(facetKey,"=",term));\r
177     stateManager.checkIn(command);\r
178     doSearch();\r
179   }\r
180   \r
181   public void setSingleTargetFilter (String targetId, String targetName) {    \r
182     if (hasSingleTargetFilter(new SingleTargetFilter(targetId,targetName))) {\r
183       logger.debug("Already using target filter " + this.singleTargetFilter.getFilterExpression());\r
184     } else {      \r
185       this.singleTargetFilter = new SingleTargetFilter(targetId,targetName);\r
186       setCommandParameter("search",new CommandParameter("filter","=",this.singleTargetFilter.getFilterExpression()));      \r
187       doSearch();\r
188     }    \r
189   }\r
190 \r
191   public SingleTargetFilter getSingleTargetFilter () {\r
192     return singleTargetFilter;\r
193   }\r
194     \r
195   public void removeSingleTargetFilter () {\r
196     logger.debug("Removing target filter " + singleTargetFilter.getFilterExpression());\r
197     this.singleTargetFilter = null;\r
198     removeCommandParameter("search","filter");         \r
199     doSearch();\r
200   }\r
201   \r
202   public boolean hasSingleTargetFilter() {\r
203     return singleTargetFilter != null;    \r
204   }\r
205         \r
206   public void setSort (String sortOption) {\r
207     logger.debug("Setting sort option: " + sortOption);\r
208     setCommandParameter("show",new CommandParameter("sort","=",sortOption));\r
209     update("show");\r
210   }\r
211   \r
212   public String getSort () {\r
213     return getCommandParameterValue("show","sort","relevance");\r
214   }\r
215     \r
216   public void setPageSize (int perPageOption) {\r
217     if (getPageSize()!=perPageOption) {\r
218      logger.debug("Setting perpage option to " + perPageOption + " and resetting start page.");\r
219      setCommandParameter("show",new CommandParameter("num","=",perPageOption));\r
220      setCommandParameter("show",new CommandParameter("start","=",0));\r
221      update("show");\r
222     } else {\r
223       logger.debug("Not updating page size, already is " + perPageOption);\r
224     }\r
225   }\r
226   \r
227   public int getPageSize () {\r
228     return getCommandParameterValue("show","num",20);\r
229   }\r
230   \r
231   public void setStart (int start) {\r
232     logger.debug("Setting start num to " + start);\r
233     setCommandParameter("show", new CommandParameter("start","=",start));  \r
234     update("show");\r
235   }\r
236   \r
237   public int getStart() {\r
238     return getCommandParameterValue("show","start",0);\r
239   }\r
240           \r
241   public String toggleRecord (String recId) {\r
242     if (hasRecord(recId)) {\r
243       removeCommand("record");  \r
244       dataObjects.put("record", new RecordResponse());\r
245       return "";\r
246     } else {\r
247       setRecordId(recId);\r
248       return doCommand("record");\r
249     }\r
250   }\r
251   \r
252   @Override\r
253   public void setRecordId(String recId) {\r
254     setCommandParameter("record",new CommandParameter("id","=",recId));\r
255   }\r
256   \r
257   @Override\r
258   public String getRecordId () {\r
259     return getCommandParameterValue("record","recid","");\r
260   }\r
261   \r
262   @Override\r
263   public boolean hasRecord (String recId) {\r
264     return getCommand("record").hasParameters() && getRecord().getRecId().equals(recId);\r
265   }\r
266       \r
267   public ShowResponse getShow () {\r
268     return ((ShowResponse) dataObjects.get("show"));\r
269   }\r
270   \r
271   public StatResponse getStat () {\r
272     return ((StatResponse) dataObjects.get("stat"));\r
273   }\r
274   \r
275   public RecordResponse getRecord() {\r
276     return ((RecordResponse) dataObjects.get("record"));\r
277   }\r
278   \r
279   public TermListsResponse getTermLists () {\r
280     return ((TermListsResponse) dataObjects.get("termlist"));\r
281   }\r
282   \r
283   public List<TermResponse> getFacetTerms (String facet, int count) {\r
284     return (getTermLists().getTermList(facet).getTerms(count));\r
285   }\r
286     \r
287   public List<TermResponse> getFacetTerms (String facet) {\r
288     return (getTermLists().getTermList(facet).getTerms());\r
289   }\r
290   \r
291   public ByTarget getByTarget() {\r
292     return ((ByTarget) dataObjects.get("bytarget"));\r
293   }\r
294   \r
295   \r
296   public String getCurrentStateKey () {    \r
297     return stateManager.getCurrentState().getKey();\r
298   }\r
299       \r
300   public void setCurrentStateKey(String key) {       \r
301     stateManager.setCurrentStateKey(key);\r
302   }\r
303   \r
304   public boolean hasConfigurationErrors () {\r
305       return (configurationErrors.size()>0);      \r
306   }\r
307   \r
308   public boolean hasCommandErrors () {\r
309     if (dataObjects.get("search").hasApplicationError()) {\r
310       logger.info("Error detected in search");\r
311       return true;\r
312     }\r
313     for (String name : dataObjects.keySet()) {\r
314       if (dataObjects.get(name).hasApplicationError()) {\r
315         logger.info("Error detected in " + name);\r
316         return true;\r
317       }\r
318     }    \r
319     return false;    \r
320   }\r
321   \r
322   /**\r
323    * Returns true if application error found in any response data objects \r
324    */\r
325   public boolean hasErrors () {\r
326     return hasConfigurationErrors() || hasCommandErrors();\r
327   }\r
328 \r
329   public List<ErrorInterface> getConfigurationErrors() {    \r
330     return configurationErrors;\r
331   }\r
332   \r
333   /**\r
334    * Returns a search command error, if any, otherwise the first\r
335    * error found for an arbitrary command, if any, otherwise\r
336    * an empty dummy error. \r
337    */    \r
338   public ErrorInterface getCommandError() {\r
339     CommandError error = new CommandError();    \r
340     if (dataObjects.get("search").hasApplicationError()) {\r
341       error = dataObjects.get("search").getApplicationError();                        \r
342     } else {\r
343       for (String name : dataObjects.keySet()) {     \r
344         if (dataObjects.get(name).hasApplicationError()) {     \r
345           error = dataObjects.get(name).getApplicationError(); \r
346           break;\r
347         } \r
348       }\r
349     }\r
350     error.setErrorHelper(errorHelper);\r
351     return error;         \r
352   }\r
353 \r
354     \r
355   protected boolean hasSingleTargetFilter(SingleTargetFilter targetFilter) {\r
356     return hasSingleTargetFilter() && targetFilter.equals(this.singleTargetFilter);\r
357   }\r
358   \r
359   protected boolean hasQuery() {\r
360     return getCommand("search").getParameter("query") != null && getCommand("search").getParameter("query").getValueWithExpressions().length()>0;\r
361   }\r
362     \r
363   public boolean hasRecords () {\r
364     return getStat().getRecords() > 0            \r
365            && getShow().getHits() != null \r
366            && getShow().getHits().size()>0;\r
367   }\r
368     \r
369   public ResultsPager getPager () {\r
370     if (pager == null) {\r
371       pager = new ResultsPager(this);      \r
372     } \r
373     return pager;      \r
374   }\r
375   \r
376   public ResultsPager setPager (int pageRange) {\r
377     pager =  new ResultsPager(this,pageRange);\r
378     return pager;\r
379   }\r
380   \r
381   protected ErrorHelper getTroubleshooter() {\r
382     return errorHelper;\r
383   }\r
384   \r
385   protected void handleQueryStateChanges (String commands) {\r
386     if (stateManager.hasPendingStateChange("search") && hasQuery()) { \r
387       logger.debug("Found pending search change. Doing search before updating " + commands);      \r
388       doSearch();\r
389     } \r
390     if (stateManager.hasPendingStateChange("record") && ! commands.equals("record")) {        \r
391       logger.debug("Found pending record ID change. Doing record before updating " + commands);\r
392       stateManager.hasPendingStateChange("record",false);\r
393       if (getCommand("record").hasParameters()) {\r
394         update("record");\r
395       } else {\r
396         removeCommand("record");  \r
397         dataObjects.put("record", new RecordResponse());\r
398       }\r
399     }    \r
400   }\r
401 \r
402   protected String getActiveClients() {    \r
403     if (getShow()!=null) {\r
404       logger.debug("Active clients: "+getShow().getActiveClients());\r
405       return getShow().getActiveClients();\r
406     } else {\r
407       return "";\r
408     }\r
409   }\r
410 \r
411   /**\r
412    * Returns a Pazpar2 command 'detached' from the current Pazpar2 state.\r
413    * \r
414    * 'Detached' is meant to imply that this is a copy of a command in the \r
415    * current state, detached so as to NOT change the current state if \r
416    * modified. It can be viewed and executed, however. \r
417    * \r
418    * In order to modify the command with effect for subsequent searches,\r
419    * it must be checked back into the StateManager, which will\r
420    * then create a new current Pazpar2 state as needed.\r
421    *  \r
422    * @param name\r
423    * @return\r
424    */\r
425   protected Pazpar2Command getCommand(String name) {\r
426     return stateManager.checkOut(name);\r
427   }\r
428   \r
429   protected void setCommandParameter(String commandName, CommandParameter parameter) {\r
430     logger.debug("Setting parameter for " + commandName + ": " + parameter);\r
431     Pazpar2Command command = getCommand(commandName);\r
432     command.setParameter(parameter);\r
433     stateManager.checkIn(command);    \r
434   }\r
435   \r
436   \r
437   protected void removeCommandParameter(String commandName, String parameterName) {\r
438     Pazpar2Command command = getCommand(commandName);\r
439     command.removeParameter(parameterName);\r
440     stateManager.checkIn(command);    \r
441   }\r
442   \r
443   protected void removeCommand (String commandName) {\r
444     Pazpar2Command command = getCommand(commandName);\r
445     command.removeParameters();\r
446     stateManager.checkIn(command);\r
447   }\r
448     \r
449   protected String getCommandParameterValue (String commandName, String parameterName, String defaultValue) {    \r
450     Pazpar2Command command = getCommand(commandName);\r
451     if (command != null) {\r
452       CommandParameter parameter = command.getParameter(parameterName);\r
453       if (parameter != null) {\r
454         return parameter.getValueWithExpressions();\r
455       }\r
456     }\r
457     return defaultValue;    \r
458   }\r
459   \r
460   protected String getCommandParameterValueSimple (String commandName, String parameterName, String defaultValue) {    \r
461     Pazpar2Command command = getCommand(commandName);\r
462     if (command != null) {\r
463       CommandParameter parameter = command.getParameter(parameterName);\r
464       if (parameter != null) {\r
465         return parameter.getSimpleValue();\r
466       }\r
467     }\r
468     return defaultValue;    \r
469   }\r
470 \r
471   \r
472   protected int getCommandParameterValue (String commandName, String parameterName, int defaultValue) {\r
473     Pazpar2Command command = getCommand(commandName);\r
474     if (command != null) {\r
475       CommandParameter parameter = command.getParameter(parameterName);\r
476       if (parameter != null) {\r
477         return Integer.parseInt(parameter.getSimpleValue());\r
478       }\r
479     }\r
480     return defaultValue;    \r
481   }\r
482 \r
483   protected String doCommand(String commandName) {     \r
484     Pazpar2Command command = getCommand(commandName);    \r
485     logger.debug(command.getEncodedQueryString() + ": Results for "+ getCommand("search").getEncodedQueryString());\r
486     return update(commandName);\r
487   }\r
488   \r
489   protected void resetDataObjects() {\r
490     logger.debug("Resetting show,stat,termlist,bytarget,search response objects.");\r
491     dataObjects = new ConcurrentHashMap<String,Pazpar2ResponseData>();\r
492     dataObjects.put("show", new ShowResponse());\r
493     dataObjects.put("stat", new StatResponse());\r
494     dataObjects.put("termlist", new TermListsResponse());\r
495     dataObjects.put("bytarget", new ByTarget());\r
496     dataObjects.put("record", new RecordResponse());\r
497     dataObjects.put("search", new SearchResponse());\r
498   }\r
499   \r
500   @Override\r
501   public void setFilter(String filterExpression) {\r
502     logger.debug("Setting filter to " + filterExpression);\r
503     setCommandParameter("search",new CommandParameter("filter","=",filterExpression));    \r
504   }\r
505   \r
506   public String getFilter() {\r
507     return getCommandParameterValueSimple("search", "filter", "");\r
508   }\r
509   \r
510   public boolean hasFilter () {\r
511     return getFilter().length()>0;\r
512   }\r
513   \r
514 }\r