Sanitary
[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.ByteArrayOutputStream;\r
4 import java.io.IOException;\r
5 import java.util.ArrayList;\r
6 import java.util.List;\r
7 import java.util.Map;\r
8 import java.util.StringTokenizer;\r
9 import java.util.concurrent.ConcurrentHashMap;\r
10 \r
11 import javax.enterprise.context.SessionScoped;\r
12 import javax.inject.Named;\r
13 \r
14 import org.apache.log4j.Logger;\r
15 \r
16 import com.indexdata.masterkey.pazpar2.client.ClientCommand;\r
17 import com.indexdata.masterkey.pazpar2.client.Pazpar2Client;\r
18 import com.indexdata.masterkey.pazpar2.client.Pazpar2ClientConfiguration;\r
19 import com.indexdata.masterkey.pazpar2.client.Pazpar2ClientGeneric;\r
20 import com.indexdata.masterkey.pazpar2.client.exceptions.Pazpar2ErrorException;\r
21 import com.indexdata.masterkey.pazpar2.client.exceptions.ProxyErrorException;\r
22 import com.indexdata.pz2utils4jsf.config.Pz2Configurator;\r
23 import com.indexdata.pz2utils4jsf.controls.ResultsPager;\r
24 import com.indexdata.pz2utils4jsf.errors.ConfigurationError;\r
25 import com.indexdata.pz2utils4jsf.errors.ConfigurationException;\r
26 import com.indexdata.pz2utils4jsf.errors.ErrorHelper;\r
27 import com.indexdata.pz2utils4jsf.errors.ErrorInterface;\r
28 import com.indexdata.pz2utils4jsf.pazpar2.data.ByTarget;\r
29 import com.indexdata.pz2utils4jsf.pazpar2.data.CommandError;\r
30 import com.indexdata.pz2utils4jsf.pazpar2.data.Pazpar2ResponseData;\r
31 import com.indexdata.pz2utils4jsf.pazpar2.data.Pazpar2ResponseParser;\r
32 import com.indexdata.pz2utils4jsf.pazpar2.data.RecordResponse;\r
33 import com.indexdata.pz2utils4jsf.pazpar2.data.SearchResponse;\r
34 import com.indexdata.pz2utils4jsf.pazpar2.data.ShowResponse;\r
35 import com.indexdata.pz2utils4jsf.pazpar2.data.StatResponse;\r
36 import com.indexdata.pz2utils4jsf.pazpar2.data.TermListsResponse;\r
37 import com.indexdata.pz2utils4jsf.pazpar2.data.TermResponse;\r
38 import com.indexdata.pz2utils4jsf.pazpar2.state.QueryStates;\r
39 import com.indexdata.pz2utils4jsf.utils.Utils;\r
40 \r
41 @Named @SessionScoped  \r
42 public class Pz2Session implements Pz2Interface {\r
43   \r
44   private static Logger logger = Logger.getLogger(Pz2Session.class);\r
45   \r
46   private Map<String,Pazpar2ResponseData> dataObjects = new ConcurrentHashMap<String,Pazpar2ResponseData>();\r
47   private QueryStates queryStates = new QueryStates();\r
48   \r
49   private static final long serialVersionUID = 3947514708343320514L;  \r
50   private Pazpar2ClientConfiguration cfg = null;\r
51   private Pazpar2Client client = null;   \r
52   private TargetFilter targetFilter = null;  \r
53   private ResultsPager pager = null; \r
54   private ErrorHelper errorHelper = null;\r
55   private List<ErrorInterface> configurationErrors = null;\r
56   \r
57   public Pz2Session () {\r
58     logger.info("Instantiating pz2 session object [" + Utils.objectId(this) + "]");      \r
59   }\r
60     \r
61   public void init(Pz2Configurator pz2conf) {\r
62     if (client==null) {\r
63       configurationErrors = new ArrayList<ErrorInterface>();\r
64       errorHelper = new ErrorHelper(pz2conf);\r
65       logger.info(Utils.objectId(this) + " is configuring itself using the provided " + Utils.objectId(pz2conf));\r
66       try {\r
67         cfg = new Pazpar2ClientConfiguration(pz2conf.getConfig());\r
68       } catch (ProxyErrorException pe) {\r
69         logger.error("Could not configure Pazpar2 client: " + pe.getMessage());\r
70         configurationErrors.add(new ConfigurationError("Pz2Client Config","ProxyError","Could not configure Pazpar2 client: " + pe.getMessage(),errorHelper));\r
71       } catch (ConfigurationException io) {\r
72         logger.error("Could not configure Pazpar2 client: " + io.getMessage());\r
73         configurationErrors.add(new ConfigurationError("Pz2Client Config","ProxyError","Could not configure Pazpar2 client: " + io.getMessage(),errorHelper));\r
74       }\r
75       if (cfg != null) {\r
76         try {\r
77           client = new Pazpar2ClientGeneric(cfg);  \r
78         } catch (ProxyErrorException pe) {\r
79           logger.error("Could not instantiate Pazpar2 client: " + pe.getMessage());\r
80           configurationErrors.add(new ConfigurationError("Pz2Client error","ProxyError","Could not create Pazpar2 client: " +pe.getMessage(),errorHelper));                \r
81         } \r
82         if (hasConfigurationErrors()) {\r
83           logger.info("Found " + configurationErrors.size() + " configuration errors");\r
84         }\r
85       }\r
86       resetDataObjects();\r
87     } else {\r
88       logger.warn("Attempt to configure session but it already has a configured client");\r
89     }\r
90   }\r
91     \r
92   public void doSearch(String query) {\r
93     setCommandParameter("search",new CommandParameter("query","=",query));     \r
94     doSearch();\r
95   }\r
96 \r
97   public void doSearch() { \r
98     queryStates.hasPendingStateChange("search",false);\r
99     resetDataObjects();\r
100     setCommandParameter("show",new CommandParameter("start","=",0));    \r
101     logger.debug(Utils.objectId(this) + " is searching using "+getCommand("search").getParameter("query").getEncodedQueryString());\r
102     doCommand("search");    \r
103   }\r
104       \r
105   /**\r
106    * Refreshes 'show', 'stat', 'termlist', and 'bytarget' data object from pazpar2\r
107    * \r
108    * @return Number of activeclients at the time of the 'show' command.\r
109    */\r
110   public String update () {\r
111     logger.debug("Updating show,stat,termlist,bytarget from pazpar2");\r
112     return update("show,stat,termlist,bytarget");\r
113   }\r
114    \r
115   /**\r
116    * Refreshes the data objects listed in 'commands' from pazpar2\r
117    * \r
118    * @param commands\r
119    * @return Number of activeclients at the time of the 'show' command\r
120    */\r
121   public String update (String commands) {\r
122     if (! hasConfigurationErrors()) {\r
123       if (hasQuery()) {\r
124         handleQueryStateChanges(commands);\r
125         logger.debug("Processing request for " + commands); \r
126         List<CommandThread> threadList = new ArrayList<CommandThread>();\r
127         StringTokenizer tokens = new StringTokenizer(commands,",");\r
128         while (tokens.hasMoreElements()) {\r
129           threadList.add(new CommandThread(getCommand(tokens.nextToken()),client));            \r
130         }\r
131         for (CommandThread thread : threadList) {\r
132           thread.start();\r
133         }\r
134         for (CommandThread thread : threadList) {\r
135           try {\r
136             thread.join();\r
137           } catch (InterruptedException e) {\r
138             e.printStackTrace();\r
139           }\r
140         }\r
141         for (CommandThread thread : threadList) {\r
142            String commandName = thread.getCommand().getName();\r
143            Pazpar2ResponseData responseObject = Pazpar2ResponseParser.getParser().getDataObject(thread.getResponse());\r
144            dataObjects.put(commandName, responseObject);        \r
145         }\r
146         return getActiveClients();\r
147       } else {\r
148         logger.debug("Skipped requests for " + commands + " as there's not yet a query."); \r
149         resetDataObjects();\r
150         return "0";\r
151       }\r
152     } else {\r
153       logger.error("Did not attempt to execute query since there are configuration errors.");\r
154       return "0";\r
155     }\r
156     \r
157   }\r
158         \r
159   public void setQuery (String query) {\r
160     logger.debug("Creating new command parameter for " + query);\r
161     setCommandParameter("search",new CommandParameter("query","=",query));\r
162   }\r
163   \r
164   public String getQuery () {\r
165     return getCommandParameterValueSimple("search","query",null);\r
166   }\r
167   \r
168   public void setFacet (String facetKey, String term) {           \r
169     if (term != null && term.length()>0) {\r
170       queryStates.getCurrentState().setCommandParameterExpression("search","query",new Expression(facetKey,"=",term),queryStates);\r
171       doSearch();\r
172     }            \r
173   }\r
174   \r
175   public void setFacetOnQuery (String facetKey, String term) {\r
176     String facetExpression = facetKey + "=" + term;    \r
177     if (term != null && term.length()>0) {\r
178       setCommandParameter("search",new CommandParameter("query","=", getQuery() + " and " + facetExpression));\r
179       doSearch();        \r
180     }            \r
181   }\r
182       \r
183   public void removeFacet(String facetKey, String term) {\r
184     queryStates.getCurrentState().removeCommandParameterExpression("search","query",new Expression(facetKey,"=",term),queryStates);\r
185     doSearch();\r
186   }\r
187   \r
188   public void setTargetFilter (String targetId, String targetName) {    \r
189     if (hasTargetFilter(new TargetFilter(targetId,targetName))) {\r
190       logger.debug("Already using target filter " + this.targetFilter.getFilterExpression());\r
191     } else {      \r
192       this.targetFilter = new TargetFilter(targetId,targetName);\r
193       setCommandParameter("search",new CommandParameter("filter","=",this.targetFilter.getFilterExpression()));      \r
194       doSearch();\r
195     }    \r
196   }\r
197 \r
198   public TargetFilter getTargetFilter () {\r
199     return targetFilter;\r
200   }\r
201     \r
202   public void removeTargetFilter () {\r
203     logger.debug("Removing target filter " + targetFilter.getFilterExpression());\r
204     this.targetFilter = null;\r
205     removeCommandParameter("search","filter");         \r
206     doSearch();\r
207   }\r
208   \r
209   public boolean hasTargetFilter() {\r
210     return targetFilter != null;    \r
211   }\r
212         \r
213   public void setSort (String sortOption) {\r
214     logger.debug("Setting sort option: " + sortOption);\r
215     setCommandParameter("show",new CommandParameter("sort","=",sortOption));\r
216     update("show");\r
217   }\r
218   \r
219   public String getSort () {\r
220     return getCommandParameterValue("show","sort","relevance");\r
221   }\r
222     \r
223   public void setPageSize (int perPageOption) {\r
224     if (getPageSize()!=perPageOption) {\r
225      logger.debug("Setting perpage option to " + perPageOption + " and resetting start page.");\r
226      setCommandParameter("show",new CommandParameter("num","=",perPageOption));\r
227      setCommandParameter("show",new CommandParameter("start","=",0));\r
228      update("show");\r
229     } else {\r
230       logger.debug("Not updating page size, already is " + perPageOption);\r
231     }\r
232   }\r
233   \r
234   public int getPageSize () {\r
235     return getCommandParameterValue("show","num",20);\r
236   }\r
237   \r
238   public void setStart (int start) {\r
239     logger.debug("Setting start num to " + start);\r
240     setCommandParameter("show", new CommandParameter("start","=",start));  \r
241     update("show");\r
242   }\r
243   \r
244   public int getStart() {\r
245     return getCommandParameterValue("show","start",0);\r
246   }\r
247         \r
248   public String toggleRecord (String recId) {\r
249     if (hasRecord(recId)) {\r
250       removeCommand("record");  \r
251       dataObjects.put("record", new RecordResponse());\r
252       return "";\r
253     } else {\r
254       return updateRecord(recId);\r
255     }\r
256   }\r
257   \r
258   private String updateRecord(String recId) {    \r
259     setCommandParameter("record",new CommandParameter("id","=",recId));    \r
260     return doCommand("record");    \r
261   }\r
262   \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 queryStates.getCurrentStateKey();\r
298   }\r
299       \r
300   public void setCurrentStateKey(String key) {       \r
301     queryStates.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   private boolean hasTargetFilter(TargetFilter targetFilter) {\r
356     return hasTargetFilter() && targetFilter.equals(this.targetFilter);\r
357   }\r
358   \r
359   private boolean hasQuery() {\r
360     return !(getCommand("search").getParameter("query") == null);\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   private void handleQueryStateChanges (String commands) {\r
386     if (queryStates.hasPendingStateChange("search")) { \r
387       logger.debug("Found pending search change. Doing search before updating " + commands);\r
388       doSearch();\r
389     } \r
390     if (queryStates.hasPendingStateChange("record") && ! commands.equals("record")) {        \r
391       logger.debug("Found pending record ID change. Doing record before updating " + commands);\r
392       queryStates.hasPendingStateChange("record",false);\r
393       if (getCommand("record").hasParameters()) {\r
394         updateRecord(getCommand("record").getParameter("id").getSimpleValue());\r
395       } else {\r
396         removeCommand("record");  \r
397         dataObjects.put("record", new RecordResponse());\r
398       }\r
399     }    \r
400   }\r
401 \r
402   private 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   private Pazpar2Command getCommand(String name) {\r
412     return queryStates.getCurrentState().getCommand(name);\r
413   }\r
414   \r
415   private void setCommandParameter(String commandName, CommandParameter parameter) {\r
416     logger.debug("Setting parameter for " + commandName + ": " + parameter);\r
417     queryStates.getCurrentState().setCommandParameter(commandName, parameter, queryStates);    \r
418   }\r
419   \r
420   \r
421   private void removeCommandParameter(String commandName, String parameterName) {\r
422     queryStates.getCurrentState().removeCommandParameter(commandName,parameterName,queryStates);    \r
423   }\r
424   \r
425   private void removeCommand (String commandName) {\r
426     queryStates.getCurrentState().removeCommand(commandName, queryStates);\r
427   }\r
428     \r
429   private String getCommandParameterValue (String commandName, String parameterName, String defaultValue) {    \r
430     Pazpar2Command command = getCommand(commandName);\r
431     if (command != null) {\r
432       CommandParameter parameter = command.getParameter(parameterName);\r
433       if (parameter != null) {\r
434         return parameter.getValueWithExpressions();\r
435       }\r
436     }\r
437     return defaultValue;    \r
438   }\r
439   \r
440   private String getCommandParameterValueSimple (String commandName, String parameterName, String defaultValue) {    \r
441     Pazpar2Command command = getCommand(commandName);\r
442     if (command != null) {\r
443       CommandParameter parameter = command.getParameter(parameterName);\r
444       if (parameter != null) {\r
445         return parameter.getSimpleValue();\r
446       }\r
447     }\r
448     return defaultValue;    \r
449   }\r
450 \r
451   \r
452   private int getCommandParameterValue (String commandName, String parameterName, int defaultValue) {\r
453     Pazpar2Command command = getCommand(commandName);\r
454     if (command != null) {\r
455       CommandParameter parameter = command.getParameter(parameterName);\r
456       if (parameter != null) {\r
457         return Integer.parseInt(parameter.getSimpleValue());\r
458       }\r
459     }\r
460     return defaultValue;    \r
461   }\r
462 \r
463   private String doCommand(String commandName) {      \r
464     Pazpar2Command command = getCommand(commandName);    \r
465     logger.debug(command.getEncodedQueryString() + ": Results for "+ getCommand("search").getEncodedQueryString());\r
466     return update(commandName);      \r
467   }\r
468   \r
469   private void resetDataObjects() {\r
470     logger.debug("Resetting show,stat,termlist,bytarget,search response objects.");\r
471     dataObjects = new ConcurrentHashMap<String,Pazpar2ResponseData>();\r
472     dataObjects.put("show", new ShowResponse());\r
473     dataObjects.put("stat", new StatResponse());\r
474     dataObjects.put("termlist", new TermListsResponse());\r
475     dataObjects.put("bytarget", new ByTarget());\r
476     dataObjects.put("record", new RecordResponse());\r
477     dataObjects.put("search", new SearchResponse());\r
478   }\r
479   \r
480 }\r