Adds support for chosing pz2 or sp service run-time
[mkjsf-moved-to-github.git] / src / main / java / com / indexdata / mkjsf / pazpar2 / Pz2Bean.java
1 package com.indexdata.mkjsf.pazpar2;\r
2 \r
3 import java.io.Serializable;\r
4 import java.util.ArrayList;\r
5 import java.util.Arrays;\r
6 import java.util.HashMap;\r
7 import java.util.List;\r
8 import java.util.Map;\r
9 import java.util.StringTokenizer;\r
10 \r
11 import javax.annotation.PostConstruct;\r
12 import javax.enterprise.context.SessionScoped;\r
13 import javax.inject.Inject;\r
14 import javax.inject.Named;\r
15 \r
16 import org.apache.log4j.Logger;\r
17 \r
18 import com.indexdata.mkjsf.config.Configurable;\r
19 import com.indexdata.mkjsf.config.Configuration;\r
20 import com.indexdata.mkjsf.config.ConfigurationReader;\r
21 import com.indexdata.mkjsf.controls.ResultsPager;\r
22 import com.indexdata.mkjsf.errors.ConfigurationError;\r
23 import com.indexdata.mkjsf.errors.ConfigurationException;\r
24 import com.indexdata.mkjsf.errors.ErrorCentral;\r
25 import com.indexdata.mkjsf.errors.ErrorHelper;\r
26 import com.indexdata.mkjsf.pazpar2.commands.CommandParameter;\r
27 import com.indexdata.mkjsf.pazpar2.commands.Pazpar2Commands;\r
28 import com.indexdata.mkjsf.pazpar2.data.Pazpar2ResponseData;\r
29 import com.indexdata.mkjsf.pazpar2.data.Pazpar2ResponseParser;\r
30 import com.indexdata.mkjsf.pazpar2.data.Pazpar2Responses;\r
31 import com.indexdata.mkjsf.pazpar2.data.RecordResponse;\r
32 import com.indexdata.mkjsf.pazpar2.sp.auth.ServiceProxyUser;\r
33 import com.indexdata.mkjsf.pazpar2.state.StateListener;\r
34 import com.indexdata.mkjsf.pazpar2.state.StateManager;\r
35 import com.indexdata.mkjsf.utils.Utils;\r
36 \r
37 @Named("pz2") @SessionScoped\r
38 public class Pz2Bean implements Pz2Interface, StateListener, Configurable, Serializable {\r
39 \r
40   private static final String MODULE_NAME = "service";\r
41   private static String SERVICE_TYPE_TBD = "TBD", SERVICE_TYPE_PZ2 = "PZ2", SERVICE_TYPE_SP = "SP";\r
42   private static final List<String> serviceTypes = \r
43                 Arrays.asList(SERVICE_TYPE_PZ2,SERVICE_TYPE_SP,SERVICE_TYPE_TBD);\r
44   private String serviceType = SERVICE_TYPE_TBD;\r
45   private List<String> serviceProxyUrls = new ArrayList<String>();\r
46   public static final String SERVICE_PROXY_URL_LIST = "SERVICE_PROXY_URL_LIST";\r
47   private List<String> pazpar2Urls = new ArrayList<String>();\r
48   public static final String PAZPAR2_URL_LIST = "PAZPAR2_URL_LIST";\r
49 \r
50 \r
51   private static final long serialVersionUID = 3440277287081557861L;\r
52   private static Logger logger = Logger.getLogger(Pz2Bean.class);\r
53   private static Logger responseLogger = Logger.getLogger("com.indexdata.mkjsf.pazpar2.responses");   \r
54   protected Pz2Client pz2Client = null;\r
55   protected ServiceProxyClient spClient = null;\r
56   protected SearchClient searchClient = null;  \r
57     \r
58   @Inject ConfigurationReader configurator;\r
59   @Inject StateManager stateMgr;\r
60   @Inject Pazpar2Commands pzreq;\r
61   @Inject Pazpar2Responses pzresp;\r
62   @Inject ErrorCentral errors;\r
63   @Inject ServiceProxyUser user;\r
64   \r
65   protected ResultsPager pager = null; \r
66   \r
67   protected ErrorHelper errorHelper = null;\r
68               \r
69   public Pz2Bean () {\r
70     logger.info("Instantiating pz2 bean [" + Utils.objectId(this) + "]");    \r
71   }\r
72   \r
73   @PostConstruct\r
74   public void postConstruct() {    \r
75     logger.debug("Pz2Bean post-construct: Configurator is " + configurator);\r
76     logger.debug(Utils.objectId(this) + " will instantiate a Pz2Client next.");\r
77     pz2Client = new Pz2Client();\r
78     configureClient(pz2Client,configurator);\r
79     spClient = new ServiceProxyClient();\r
80     configureClient(spClient,configurator);\r
81     try {\r
82       this.configure(configurator);\r
83     } catch (ConfigurationException e) {\r
84       logger.error("There was a problem configuring the Pz2Bean (\"pz2\")");\r
85       e.printStackTrace();\r
86     }    \r
87     stateMgr.addStateListener(this);    \r
88   }  \r
89   \r
90   public void configureClient(SearchClient client, ConfigurationReader configReader) {\r
91     logger.debug(Utils.objectId(this) + " will configure search client for the session");\r
92     try {\r
93       client.configure(configReader);            \r
94     } catch (ConfigurationException e) {\r
95       logger.debug("Pz2Bean adding configuration error");\r
96       errors.addConfigurationError(new ConfigurationError("Search Client","Configuration",e.getMessage()));                \r
97     } \r
98     logger.info(configReader.document());\r
99     pzresp.reset();    \r
100   }\r
101   \r
102   public void resetSearchAndResults () {\r
103     pzreq.getRecord().removeParametersInState();\r
104     pzreq.getSearch().removeParametersInState();\r
105     pzresp.reset();    \r
106   }\r
107 \r
108     \r
109   public void doSearch(String query) {\r
110     pzreq.getSearch().setParameter(new CommandParameter("query","=",query));     \r
111     doSearch();\r
112   }\r
113 \r
114   public void doSearch() { \r
115     stateMgr.hasPendingStateChange("search",false);\r
116     pzresp.reset();\r
117     // resets some record and show command parameters without \r
118     // changing state or creating state change feedback\r
119     pzreq.getRecord().removeParametersInState();        \r
120     pzreq.getShow().setParameterInState(new CommandParameter("start","=",0));    \r
121     logger.debug(Utils.objectId(this) + " is searching using "+pzreq.getCommand("search").getUrlEncodedParameterValue("query"));\r
122     doCommand("search");    \r
123   }\r
124       \r
125   /**\r
126    * Refreshes 'show', 'stat', 'termlist', and 'bytarget' data object from pazpar2\r
127    * \r
128    * @return Number of activeclients at the time of the 'show' command.\r
129    */\r
130   public String update () {\r
131     logger.debug("Updating show,stat,termlist,bytarget from pazpar2");\r
132     return update("show,stat,termlist,bytarget");\r
133   }\r
134    \r
135   /**\r
136    * Refreshes the data objects listed in 'commands' from pazpar2\r
137    * \r
138    * @param commands\r
139    * @return Number of activeclients at the time of the 'show' command\r
140    */\r
141   public String update (String commands) {\r
142     try {\r
143     if (! errors.hasConfigurationErrors()) {\r
144       if (commandsAreValid(commands)) {\r
145         if (hasQuery() || (commands.equals("record") && pzreq.getCommand("record").hasParameterValue("recordquery"))) {\r
146           handleQueryStateChanges(commands);\r
147           logger.debug("Processing request for " + commands); \r
148           List<CommandThread> threadList = new ArrayList<CommandThread>();\r
149           StringTokenizer tokens = new StringTokenizer(commands,",");\r
150           while (tokens.hasMoreElements()) {          \r
151             threadList.add(new CommandThread(pzreq.getCommand(tokens.nextToken()),searchClient));            \r
152           }\r
153           for (CommandThread thread : threadList) {\r
154             thread.start();\r
155           }\r
156           for (CommandThread thread : threadList) {\r
157             try {\r
158               thread.join();\r
159             } catch (InterruptedException e) {\r
160               e.printStackTrace();\r
161             }\r
162           }\r
163           for (CommandThread thread : threadList) {\r
164              String commandName = thread.getCommand().getCommandName();\r
165              CommandResponse response = thread.getCommandResponse();\r
166              responseLogger.debug("Response was: " + response.getResponseString());\r
167              Pazpar2ResponseData responseObject = Pazpar2ResponseParser.getParser().getDataObject(response.getResponseString());\r
168              if (Pazpar2ResponseParser.docTypes.contains(responseObject.getType())) {\r
169                pzresp.put(commandName, responseObject);\r
170              } else {\r
171                if (commandName.equals("record") && \r
172                    (pzreq.getRecord().hasParameterValue("offset") ||\r
173                     pzreq.getRecord().hasParameterValue("checksum"))) {\r
174                  RecordResponse recordResponse = new RecordResponse();\r
175                  recordResponse.setType("record");\r
176                  recordResponse.setXml(responseObject.getXml());\r
177                  recordResponse.setAttribute("activeclients", "0");\r
178                  pzresp.put(commandName, recordResponse);\r
179                }\r
180              }\r
181           }\r
182           if (commands.equals("record")) {\r
183             return pzresp.getRecord().getActiveClients();\r
184           } else {\r
185             return pzresp.getActiveClients();\r
186           }  \r
187         } else {\r
188           logger.debug("Skipped requests for " + commands + " as there's not yet a query."); \r
189           pzresp.reset();\r
190           return "0";\r
191         }\r
192       } else {\r
193         logger.error("Did not attemt to run command(s) due to a validation error.");\r
194         return "0";\r
195       }\r
196     } else {      \r
197       logger.error("Did not attempt to execute query since there are configuration errors.");\r
198       return "0";\r
199     }\r
200     } catch (ClassCastException cce) {\r
201       cce.printStackTrace();    \r
202       return "";\r
203     } catch (NullPointerException npe) {\r
204       npe.printStackTrace();\r
205       return "";\r
206     } catch (Exception e) {\r
207       e.printStackTrace();\r
208       return "";\r
209     }\r
210     \r
211   }\r
212   \r
213   public boolean commandsAreValid(String commands) {\r
214     if (commands.equals("record")) {\r
215       if (!pzreq.getCommand("record").hasParameterValue("id")) {\r
216         logger.error("Attempt to send record command without the id parameter");\r
217         return false;\r
218       }\r
219     }\r
220     return true;\r
221   }\r
222                                 \r
223   public String toggleRecord (String recId) {\r
224     if (hasRecord(recId)) {\r
225       pzreq.getRecord().removeParameters();  \r
226       pzresp.put("record", new RecordResponse());\r
227       return "";\r
228     } else {\r
229       pzreq.getRecord().setId(recId);\r
230       return doCommand("record");\r
231     }\r
232   }\r
233   \r
234   @Override\r
235   public boolean hasRecord (String recId) {\r
236     return pzreq.getCommand("record").hasParameters() && pzresp.getRecord().getRecId().equals(recId);\r
237   }\r
238         \r
239   public String getCurrentStateKey () {    \r
240     return stateMgr.getCurrentState().getKey();\r
241   }\r
242       \r
243   public void setCurrentStateKey(String key) {       \r
244     stateMgr.setCurrentStateKey(key);\r
245   }\r
246       \r
247   protected boolean hasQuery() {        \r
248     return pzreq.getCommand("search").hasParameterValue("query"); \r
249   }\r
250     \r
251     \r
252   @Override\r
253   public ResultsPager getPager () {\r
254     if (pager == null) {\r
255       pager = new ResultsPager(pzresp);      \r
256     } \r
257     return pager;      \r
258   }\r
259   \r
260   @Override\r
261   public ResultsPager setPager (int pageRange) {\r
262     pager =  new ResultsPager(pzresp,pageRange,pzreq);\r
263     return pager;\r
264   }\r
265     \r
266   protected void handleQueryStateChanges (String commands) {\r
267     if (stateMgr.hasPendingStateChange("search") && hasQuery()) { \r
268       logger.debug("Found pending search change. Doing search before updating " + commands);      \r
269       doSearch();\r
270     } \r
271     if (stateMgr.hasPendingStateChange("record") && ! commands.equals("record")) {        \r
272       logger.debug("Found pending record ID change. Doing record before updating " + commands);\r
273       stateMgr.hasPendingStateChange("record",false);\r
274       if (pzreq.getCommand("record").hasParameterValue("id")) {\r
275         update("record");\r
276       } else {         \r
277         pzresp.put("record", new RecordResponse());\r
278       }\r
279     }\r
280   }\r
281   \r
282   protected String doCommand(String commandName) {             \r
283     logger.debug(pzreq.getCommand(commandName).getEncodedQueryString() + ": Results for "+ pzreq.getCommand("search").getEncodedQueryString());\r
284     return update(commandName);\r
285   }\r
286   \r
287   @Override\r
288   public void stateUpdated(String commandName) {\r
289     logger.debug("State change reported for [" + commandName + "]");\r
290     if (commandName.equals("show")) {\r
291       logger.debug("Updating show");\r
292       update(commandName);\r
293     } \r
294   }\r
295   \r
296   public void setServiceProxyUrl(String url) {\r
297     searchClient = spClient;\r
298     setServiceUrl(url);\r
299   }\r
300   \r
301   public String getServiceProxyUrl () {\r
302     return spClient.getServiceUrl();\r
303   }\r
304   \r
305   public void setPazpar2Url(String url) {\r
306     searchClient = pz2Client;\r
307     setServiceUrl(url);\r
308   }\r
309   \r
310   public String getPazpar2Url() {\r
311     return pz2Client.getServiceUrl();\r
312   }\r
313 \r
314   \r
315   @Override\r
316   public void setServiceUrl(String url) {\r
317     if (url!=null && searchClient != null && !url.equals(searchClient.getServiceUrl())) {\r
318       pzreq.getRecord().removeParametersInState();\r
319       pzreq.getSearch().removeParametersInState();\r
320       pzresp.reset();\r
321       user.clear();\r
322       searchClient.setServiceUrl(url);\r
323     }    \r
324   }\r
325   \r
326   public String getServiceUrl() {\r
327     return (searchClient!=null ? searchClient.getServiceUrl() : "");\r
328   }\r
329   \r
330   public boolean getServiceUrlIsDefined() {\r
331     return (searchClient != null && searchClient.hasServiceUrl());\r
332   }\r
333   \r
334   public List<String> getServiceProxyUrls() {\r
335     List<String> urls = new ArrayList<String>();\r
336     urls.add("");\r
337     urls.addAll(serviceProxyUrls);\r
338     return urls;\r
339   }\r
340   \r
341   public List<String> getPazpar2Urls () {\r
342     List<String> urls = new ArrayList<String>();\r
343     urls.add("");\r
344     urls.addAll(pazpar2Urls);\r
345     return urls;\r
346   }\r
347   \r
348   public String getServiceType () {\r
349     return serviceType;\r
350   }\r
351   \r
352   public boolean isPazpar2Service () {\r
353     return serviceType.equals(SERVICE_TYPE_PZ2);\r
354   }\r
355   \r
356   public boolean isServiceProxyService() {\r
357     return serviceType.equals(SERVICE_TYPE_SP);\r
358   }\r
359   \r
360   public boolean serviceIsToBeDecided () {\r
361     return serviceType.equals(SERVICE_TYPE_TBD);\r
362   }\r
363   \r
364   public ServiceProxyClient getSpClient () {\r
365     return spClient;\r
366   }  \r
367   \r
368   @Override\r
369   public boolean getAuthenticationRequired () {\r
370     return spClient.isAuthenticatingClient();\r
371   }\r
372 \r
373   @Override\r
374   public String getCheckHistory () {\r
375     return ":pz2watch:stateForm:windowlocationhash";\r
376   }\r
377     \r
378   @Override\r
379   public String getWatchActiveclients () {\r
380     return ":pz2watch:activeclientsForm:activeclientsField";\r
381   }\r
382   \r
383   @Override\r
384   public String getWatchActiveclientsRecord () {\r
385     return ":pz2watch:activeclientsForm:activeclientsFieldRecord";\r
386   }\r
387 \r
388   @Override\r
389   public void configure(ConfigurationReader reader)\r
390       throws ConfigurationException {\r
391     Configuration config = reader.getConfiguration(this);\r
392     if (config == null) {\r
393       serviceType = SERVICE_TYPE_TBD;\r
394     } else {\r
395       String service = config.get("TYPE");\r
396       if (service == null || service.length()==0) {\r
397         serviceType = SERVICE_TYPE_TBD;\r
398       } else if (serviceTypes.contains(service.toUpperCase())) {        \r
399         setServiceType(service.toUpperCase());\r
400       } else {\r
401         logger.error("Unknown serviceType type in configuration [" + service + "], can be one of " + serviceTypes);\r
402         serviceType = SERVICE_TYPE_TBD;\r
403       }\r
404       serviceProxyUrls = config.getMultiProperty(SERVICE_PROXY_URL_LIST,",");\r
405       pazpar2Urls = config.getMultiProperty(PAZPAR2_URL_LIST, ",");\r
406     }\r
407     logger.info("Service Type is configured to " + serviceType);\r
408     \r
409   }\r
410 \r
411   @Override\r
412   public Map<String, String> getDefaults() {\r
413     return new HashMap<String,String>();\r
414   }\r
415 \r
416   @Override\r
417   public String getModuleName() {\r
418     return MODULE_NAME;\r
419   }\r
420 \r
421   @Override\r
422   public List<String> documentConfiguration() {\r
423     return new ArrayList<String>();\r
424   }\r
425 \r
426   @Override\r
427   public void setServiceTypePZ2() {\r
428     setServiceType(SERVICE_TYPE_PZ2);    \r
429   }\r
430 \r
431   @Override\r
432   public void setServiceTypeSP() {\r
433     setServiceType(SERVICE_TYPE_SP);        \r
434   }\r
435 \r
436   @Override\r
437   public void setServiceTypeTBD() {\r
438     setServiceType(SERVICE_TYPE_TBD);    \r
439   }\r
440   \r
441   private void setServiceType(String type) {\r
442     if (!serviceType.equals(type)  &&\r
443         !serviceType.equals(SERVICE_TYPE_TBD)) {\r
444       resetSearchAndResults();\r
445     }\r
446     serviceType = type;\r
447     if (serviceType.equals(SERVICE_TYPE_PZ2)) {\r
448       searchClient = pz2Client;\r
449       logger.info("Setting a Pazpar2 client to serve requests.");\r
450     } else if (serviceType.equals(SERVICE_TYPE_SP)) {\r
451       searchClient = spClient;\r
452       logger.info("Setting a Service Proxy client to serve requests.");\r
453     } else {\r
454       logger.info("Clearing search client. No client defined to serve requests at this point.");\r
455       searchClient = null;\r
456     }\r
457   }\r
458   \r
459 }\r