X-Git-Url: http://git.indexdata.com/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fcom%2Findexdata%2Fmkjsf%2Fpazpar2%2FPz2Bean.java;h=05744cdb736951cdfe13836dfb75b35406930371;hb=11ff48b9d37193a89a4b74da75db8c58415c46a6;hp=98f4c718fb5f5c5fdf6d5c41476394552e9d8587;hpb=28b5abb240eda1749703712a0ba19a5843f84ebc;p=mkjsf-moved-to-github.git diff --git a/src/main/java/com/indexdata/mkjsf/pazpar2/Pz2Bean.java b/src/main/java/com/indexdata/mkjsf/pazpar2/Pz2Bean.java index 98f4c71..05744cd 100644 --- a/src/main/java/com/indexdata/mkjsf/pazpar2/Pz2Bean.java +++ b/src/main/java/com/indexdata/mkjsf/pazpar2/Pz2Bean.java @@ -1,18 +1,27 @@ package com.indexdata.mkjsf.pazpar2; import java.io.Serializable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.StringTokenizer; import javax.annotation.PostConstruct; import javax.enterprise.context.SessionScoped; -import javax.enterprise.inject.Alternative; +import javax.enterprise.inject.Produces; +import javax.faces.context.FacesContext; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Qualifier; import org.apache.log4j.Logger; +import com.indexdata.mkjsf.config.Configurable; +import com.indexdata.mkjsf.config.Configuration; import com.indexdata.mkjsf.config.ConfigurationReader; import com.indexdata.mkjsf.controls.ResultsPager; import com.indexdata.mkjsf.errors.ConfigurationError; @@ -20,32 +29,45 @@ import com.indexdata.mkjsf.errors.ConfigurationException; import com.indexdata.mkjsf.errors.ErrorCentral; import com.indexdata.mkjsf.errors.ErrorHelper; import com.indexdata.mkjsf.pazpar2.commands.CommandParameter; +import com.indexdata.mkjsf.pazpar2.commands.Pazpar2Command; import com.indexdata.mkjsf.pazpar2.commands.Pazpar2Commands; -import com.indexdata.mkjsf.pazpar2.data.Pazpar2ResponseData; -import com.indexdata.mkjsf.pazpar2.data.Pazpar2ResponseParser; -import com.indexdata.mkjsf.pazpar2.data.Pazpar2Responses; import com.indexdata.mkjsf.pazpar2.data.RecordResponse; +import com.indexdata.mkjsf.pazpar2.data.ResponseDataObject; +import com.indexdata.mkjsf.pazpar2.data.ResponseParser; +import com.indexdata.mkjsf.pazpar2.data.Responses; import com.indexdata.mkjsf.pazpar2.state.StateListener; import com.indexdata.mkjsf.pazpar2.state.StateManager; import com.indexdata.mkjsf.utils.Utils; -@Named("pz2") @SessionScoped @Alternative -public class Pz2Bean implements Pz2Interface, StateListener, Serializable { +@Named("pz2") @SessionScoped +public class Pz2Bean implements Pz2Interface, StateListener, Configurable, Serializable { + + private static final String MODULE_NAME = "service"; + private static String SERVICE_TYPE_TBD = "TBD", SERVICE_TYPE_PZ2 = "PZ2", SERVICE_TYPE_SP = "SP"; + private static final List serviceTypes = + Arrays.asList(SERVICE_TYPE_PZ2,SERVICE_TYPE_SP,SERVICE_TYPE_TBD); + private String serviceType = SERVICE_TYPE_TBD; + private List serviceProxyUrls = new ArrayList(); + public static final String SERVICE_PROXY_URL_LIST = "SERVICE_PROXY_URL_LIST"; + private List pazpar2Urls = new ArrayList(); + public static final String PAZPAR2_URL_LIST = "PAZPAR2_URL_LIST"; + private static final long serialVersionUID = 3440277287081557861L; private static Logger logger = Logger.getLogger(Pz2Bean.class); - private static Logger responseLogger = Logger.getLogger("com.indexdata.mkjsf.pazpar2.responses"); - - protected SearchClient searchClient = null; - + private static Logger responseLogger = Logger.getLogger("com.indexdata.mkjsf.pazpar2.responses"); + protected Pz2Client pz2Client = null; + protected ServiceProxyClient spClient = null; + protected SearchClient searchClient = null; + @Inject ConfigurationReader configurator; - @Inject StateManager stateMgr; - @Inject Pazpar2Commands pzreq; - @Inject Pazpar2Responses pzresp; - @Inject ErrorCentral errors; + + private StateManager stateMgr = null; + private Pazpar2Commands pzreq = null; + private Responses pzresp = null; + private ErrorCentral errors = null; protected ResultsPager pager = null; - protected ErrorHelper errorHelper = null; @@ -53,44 +75,122 @@ public class Pz2Bean implements Pz2Interface, StateListener, Serializable { logger.info("Instantiating pz2 bean [" + Utils.objectId(this) + "]"); } + public static Pz2Bean get() { + FacesContext context = FacesContext.getCurrentInstance(); + return (Pz2Bean) context.getApplication().evaluateExpressionGet(context, "#{pz2}", Object.class); + } + @PostConstruct - public void postConstruct() { - logger.debug("in start of Pz2Bean post-construct configurator is " + configurator); + public void postConstruct() { + logger.info("Pz2Bean PostConstruct of " + this); + stateMgr = new StateManager(); + pzreq = new Pazpar2Commands(); + pzresp = new Responses(); + errors = new ErrorCentral(); + pzresp.setErrorHelper(errors.getHelper()); + + logger.debug("Pz2Bean PostConstruct: Configurator is " + configurator); logger.debug(Utils.objectId(this) + " will instantiate a Pz2Client next."); - searchClient = new Pz2Client(); - logger.info("Using [" + Utils.objectId(searchClient) + "] configured by [" - + Utils.objectId(configurator) + "]" ); - configureClient(searchClient,configurator); + pz2Client = new Pz2Client(); + configureClient(pz2Client,configurator); + spClient = new ServiceProxyClient(); + configureClient(spClient,configurator); + try { + this.configure(configurator); + } catch (ConfigurationException e) { + logger.error("There was a problem configuring the Pz2Bean (\"pz2\")"); + e.printStackTrace(); + } stateMgr.addStateListener(this); - } + } + + @Qualifier + @Target({java.lang.annotation.ElementType.TYPE, + java.lang.annotation.ElementType.METHOD, + java.lang.annotation.ElementType.PARAMETER, + java.lang.annotation.ElementType.FIELD}) + @Retention(java.lang.annotation.RetentionPolicy.RUNTIME) + public @interface Preferred{} + + @Produces @Preferred @SessionScoped @Named("pzresp") public Responses getPzresp () { + logger.info("Producing pzresp"); + return pzresp; + } + + @Produces @Preferred @SessionScoped @Named("pzreq") public Pazpar2Commands getPzreq () { + logger.info("Producing pzreq"); + return pzreq; + } - public void configureClient(SearchClient searchClient, ConfigurationReader configReader) { + @Produces @Preferred @SessionScoped @Named("errors") public ErrorCentral getErrors() { + logger.info("Producing errors"); + return errors; + } + + @Produces @Preferred @SessionScoped @Named("stateMgr") public StateManager getStateMgr() { + logger.info("Produces stateMgr"); + return stateMgr; + } + + + public void configureClient(SearchClient client, ConfigurationReader configReader) { logger.debug(Utils.objectId(this) + " will configure search client for the session"); try { - searchClient.configure(configReader); + client.configure(configReader); } catch (ConfigurationException e) { logger.debug("Pz2Bean adding configuration error"); errors.addConfigurationError(new ConfigurationError("Search Client","Configuration",e.getMessage())); } logger.info(configReader.document()); - pzresp.reset(); + pzresp.getSp().resetAuthAndBeyond(true); + } + + public void resetSearchAndRecordCommands () { + pzreq.getRecord().removeParametersInState(); + pzreq.getSearch().removeParametersInState(); } - public void doSearch(String query) { pzreq.getSearch().setParameter(new CommandParameter("query","=",query)); doSearch(); } - public void doSearch() { - stateMgr.hasPendingStateChange("search",false); - pzresp.reset(); - // resets some record and show command parameters without - // changing state or creating state change feedback - pzreq.getRecord().removeParametersInState(); - pzreq.getShow().setParameterInState(new CommandParameter("start","=",0)); - logger.debug(Utils.objectId(this) + " is searching using "+pzreq.getCommand("search").getUrlEncodedParameterValue("query")); - doCommand("search"); + public void doSearch() { + try { + if (errors.hasConfigurationErrors()) { + logger.error("Ignoring search request due to configuration errors."); + } else if (searchClient == null){ + logger.error("No search client defined. A client must either be pre-configured or selected before searching."); + errors.addConfigurationError(new ConfigurationError("No client defined","Client is null","No search client defined. A client must be pre-configured or selected runtime, prior to searching.")); + } else { + stateMgr.hasPendingStateChange("search",false); + pzresp.resetSearchAndBeyond(); + pzreq.getRecord().removeParametersInState(); + pzreq.getShow().setParameterInState(new CommandParameter("start","=",0)); + logger.debug(Utils.objectId(this) + " is searching using "+pzreq.getCommand("search").getUrlEncodedParameterValue("query")); + searchClient.setSearchCommand(pzreq.getCommand("search")); + doCommand("search"); + } + } catch (NullPointerException npe) { + npe.printStackTrace(); + } + } + + public String doRecord() { + if (errors.hasConfigurationErrors()) { + logger.error("Ignoring record request due to configuration errors."); + return ""; + } else if (!pzreq.getCommand("record").hasParameterValue("id")) { + logger.debug("Ignoring record request due to no id parameter."); + return ""; + } else if (pzresp.getSearch().hasApplicationError()) { + logger.debug("Ignoring record request due search error."); + return ""; + } else { + logger.debug("Executing record command"); + doCommand("record"); + return pzresp.getRecord().getActiveClients(); + } } /** @@ -100,20 +200,49 @@ public class Pz2Bean implements Pz2Interface, StateListener, Serializable { */ public String update () { logger.debug("Updating show,stat,termlist,bytarget from pazpar2"); - return update("show,stat,termlist,bytarget"); + if (errors.hasConfigurationErrors()) { + logger.error("Ignoring show,stat,termlist,bytarget commands due to configuration errors."); + return ""; + } else if (pzresp.getSearch().hasApplicationError()) { + logger.error("Ignoring show,stat,termlist,bytarget commands due to problem with most recent search."); + return ""; + } else if (!hasQuery()) { + logger.debug("Ignoring show,stat,termlist,bytarget commands because there is not yet a query."); + return ""; + } else { + return update("show,stat,termlist,bytarget"); + } } - + /** * Refreshes the data objects listed in 'commands' from pazpar2 * * @param commands - * @return Number of activeclients at the time of the 'show' command + * @return Number of activeclients at the time of the 'show' command, + * or 'new' if search was just initiated. */ public String update (String commands) { - if (! errors.hasConfigurationErrors()) { - if (commandsAreValid(commands)) { - if (hasQuery() || (commands.equals("record") && pzreq.getCommand("record").hasParameterSet("recordquery"))) { - handleQueryStateChanges(commands); + logger.debug("Request to update: " + commands); + try { + if (commands.equals("search")) { + doSearch(); + return "new"; + } else if (commands.equals("record")) { + return doRecord(); + } else if (pzresp.getSearch().isNew()) { + // For returning notification of 'search started' quickly to UI + logger.info("New search. Marking it old, then returning 'new' to trigger another round-trip."); + pzresp.getSearch().setIsNew(false); + return "new"; + } else { + handleQueryStateChanges(commands); + if (pzresp.getSearch().hasApplicationError()) { + logger.error("The command(s) " + commands + " cancelled because the latest search command had an error."); + return "0"; + } else if (errors.hasConfigurationErrors()) { + logger.error("The command(s) " + commands + " cancelled due to configuration errors."); + return "0"; + } else { logger.debug("Processing request for " + commands); List threadList = new ArrayList(); StringTokenizer tokens = new StringTokenizer(commands,","); @@ -131,44 +260,32 @@ public class Pz2Bean implements Pz2Interface, StateListener, Serializable { } } for (CommandThread thread : threadList) { - String commandName = thread.getCommand().getName(); - String response = thread.getResponse(); - responseLogger.debug("Response was: " + response); - Pazpar2ResponseData responseObject = Pazpar2ResponseParser.getParser().getDataObject(response); - pzresp.put(commandName, responseObject); + String commandName = thread.getCommand().getCommandName(); + ClientCommandResponse response = (ClientCommandResponse) thread.getCommandResponse(); + responseLogger.debug("Response was: " + response.getResponseString()); + ResponseDataObject responseObject = ResponseParser.getParser().getDataObject(response); + if (ResponseParser.docTypes.contains(responseObject.getType())) { + pzresp.put(commandName, responseObject); + } else { + logger.info("Unknown doc type [" + responseObject.getType() + "]. Was not cached."); + } } - if (commands.equals("record")) { - logger.debug("Record: Active clients: "+pzresp.getRecord().getActiveClients()); - return pzresp.getRecord().getActiveClients(); - } else { - return pzresp.getActiveClients(); - } - } else { - logger.debug("Skipped requests for " + commands + " as there's not yet a query."); - pzresp.reset(); - return "0"; + return pzresp.getActiveClients(); } - } else { - logger.error("Did not attemt to run command(s) due to a validation error."); - return "0"; - } - } else { - logger.error("Did not attempt to execute query since there are configuration errors."); - return "0"; + } + } catch (ClassCastException cce) { + cce.printStackTrace(); + return ""; + } catch (NullPointerException npe) { + npe.printStackTrace(); + return ""; + } catch (Exception e) { + e.printStackTrace(); + return ""; } } - - public boolean commandsAreValid(String commands) { - if (commands.equals("record")) { - if (!pzreq.getCommand("record").hasParameterSet("id")) { - logger.error("Attempt to send record command without the id parameter"); - return false; - } - } - return true; - } - + public String toggleRecord (String recId) { if (hasRecord(recId)) { pzreq.getRecord().removeParameters(); @@ -176,7 +293,8 @@ public class Pz2Bean implements Pz2Interface, StateListener, Serializable { return ""; } else { pzreq.getRecord().setId(recId); - return doCommand("record"); + doCommand("record"); + return pzresp.getRecord().getActiveClients(); } } @@ -194,7 +312,7 @@ public class Pz2Bean implements Pz2Interface, StateListener, Serializable { } protected boolean hasQuery() { - return pzreq.getCommand("search").hasParameterSet("query"); + return pzreq.getCommand("search").hasParameterValue("query"); } @@ -211,28 +329,57 @@ public class Pz2Bean implements Pz2Interface, StateListener, Serializable { pager = new ResultsPager(pzresp,pageRange,pzreq); return pager; } - + + /** + * This methods main purpose is to support browser history. + * + * When the browsers back or forward buttons are pressed, a + * re-search /might/ be required - namely if the query changes. + * So, as the UI requests updates of the page (show,facets, + * etc) this method checks if a search must be executed + * before those updates are performed. + * + * @see {@link com.indexdata.mkjsf.pazpar2.state.StateManager#setCurrentStateKey} + * @param commands + */ protected void handleQueryStateChanges (String commands) { if (stateMgr.hasPendingStateChange("search") && hasQuery()) { - logger.debug("Found pending search change. Doing search before updating " + commands); + logger.info("Triggered search: Found pending search change [" + pzreq.getCommand("search").toString() + "], doing search before updating " + commands); doSearch(); } if (stateMgr.hasPendingStateChange("record") && ! commands.equals("record")) { logger.debug("Found pending record ID change. Doing record before updating " + commands); stateMgr.hasPendingStateChange("record",false); - if (pzreq.getCommand("record").hasParameterSet("id")) { - update("record"); + if (pzreq.getCommand("record").hasParameterValue("id")) { + doRecord(); } else { pzresp.put("record", new RecordResponse()); } } } - protected String doCommand(String commandName) { - logger.debug(pzreq.getCommand(commandName).getEncodedQueryString() + ": Results for "+ pzreq.getCommand("search").getEncodedQueryString()); - return update(commandName); + /** + * Executes the command and parses the response to create data objects. + * If the parsed response is of a known type it will be cached in 'pzresp' + * + * @param commandName The command to be executed + * @return An XML response parsed to form a response data object + */ + protected ResponseDataObject doCommand(String commandName) { + Pazpar2Command command = pzreq.getCommand(commandName); + if (command.spOnly() && isPazpar2Service()) { + logger.warn("Skipping " + commandName + " - SP-only command, not supported by Pazpar2"); + return new ResponseDataObject(); + } else { + logger.info("Request "+commandName + ". Search command is: "+ pzreq.getCommand("search").toString()); + long start = System.currentTimeMillis(); + ResponseDataObject responseObject = command.run(); + long end = System.currentTimeMillis(); + logger.debug("Executed " + command.getCommandName() + " in " + (end-start) + " ms." ); + return responseObject; + } } - + @Override public void stateUpdated(String commandName) { logger.debug("State change reported for [" + commandName + "]"); @@ -242,9 +389,102 @@ public class Pz2Bean implements Pz2Interface, StateListener, Serializable { } } + public void setServiceProxyUrl(String url) { + searchClient = spClient; + setServiceType(SERVICE_TYPE_SP); + setServiceUrl(url); + } + + public String getServiceProxyUrl () { + if (isServiceProxyService()) { + return spClient.getServiceUrl(); + } else { + return ""; + } + } + + public void setPazpar2Url(String url) { + searchClient = pz2Client; + setServiceType(SERVICE_TYPE_PZ2); + setServiceUrl(url); + } + + public String getPazpar2Url() { + if (isPazpar2Service()) { + return pz2Client.getServiceUrl(); + } else { + return ""; + } + } + + + @Override + public void setServiceUrl(String url) { + if (url!=null && searchClient != null && !url.equals(searchClient.getServiceUrl())) { + pzreq.getRecord().removeParametersInState(); + pzreq.getSearch().removeParametersInState(); + pzresp.getSp().resetAuthAndBeyond(true); + searchClient.setServiceUrl(url); + // pzreq.setService(this); + } + } + + public String getServiceUrl() { + return (searchClient!=null ? searchClient.getServiceUrl() : ""); + } + + public void setServiceId () { + pzreq.getRecord().removeParametersInState(); + pzreq.getSearch().removeParametersInState(); + pzresp.resetSearchAndBeyond(); + pz2Client.setServiceId(pzreq.getInit().getService()); + } + + public String getServiceId () { + return pzreq.getInit().getService(); + } + + public boolean getServiceUrlIsDefined() { + return (searchClient != null && searchClient.hasServiceUrl()); + } + + public List getServiceProxyUrls() { + List urls = new ArrayList(); + urls.add(""); + urls.addAll(serviceProxyUrls); + return urls; + } + + public List getPazpar2Urls () { + List urls = new ArrayList(); + urls.add(""); + urls.addAll(pazpar2Urls); + return urls; + } + + public String getServiceType () { + return serviceType; + } + + public boolean isPazpar2Service () { + return serviceType.equals(SERVICE_TYPE_PZ2); + } + + public boolean isServiceProxyService() { + return serviceType.equals(SERVICE_TYPE_SP); + } + + public boolean serviceIsToBeDecided () { + return serviceType.equals(SERVICE_TYPE_TBD); + } + + public ServiceProxyClient getSpClient () { + return spClient; + } + @Override public boolean getAuthenticationRequired () { - return searchClient.isAuthenticatingClient(); + return spClient.isAuthenticatingClient(); } @Override @@ -261,5 +501,82 @@ public class Pz2Bean implements Pz2Interface, StateListener, Serializable { public String getWatchActiveclientsRecord () { return ":pz2watch:activeclientsForm:activeclientsFieldRecord"; } + + @Override + public void configure(ConfigurationReader reader) + throws ConfigurationException { + Configuration config = reader.getConfiguration(this); + if (config == null) { + serviceType = SERVICE_TYPE_TBD; + } else { + String service = config.get("TYPE"); + if (service == null || service.length()==0) { + serviceType = SERVICE_TYPE_TBD; + } else if (serviceTypes.contains(service.toUpperCase())) { + setServiceType(service.toUpperCase()); + } else { + logger.error("Unknown serviceType type in configuration [" + service + "], can be one of " + serviceTypes); + serviceType = SERVICE_TYPE_TBD; + } + serviceProxyUrls = config.getMultiProperty(SERVICE_PROXY_URL_LIST,","); + pazpar2Urls = config.getMultiProperty(PAZPAR2_URL_LIST, ","); + } + logger.info(reader.document()); + logger.info("Service Type is configured to " + serviceType); + + } + + @Override + public Map getDefaults() { + return new HashMap(); + } + + @Override + public String getModuleName() { + return MODULE_NAME; + } + + @Override + public List documentConfiguration() { + return new ArrayList(); + } + + @Override + public void setServiceTypePZ2() { + setServiceType(SERVICE_TYPE_PZ2); + } + + @Override + public void setServiceTypeSP() { + setServiceType(SERVICE_TYPE_SP); + } + + @Override + public void setServiceTypeTBD() { + setServiceType(SERVICE_TYPE_TBD); + } + + private void setServiceType(String type) { + if (!serviceType.equals(type) && + !serviceType.equals(SERVICE_TYPE_TBD)) { + resetSearchAndRecordCommands(); + pzresp.getSp().resetAuthAndBeyond(true); + } + serviceType = type; + if (serviceType.equals(SERVICE_TYPE_PZ2)) { + searchClient = pz2Client; + logger.info("Setting a Pazpar2 client to serve requests."); + } else if (serviceType.equals(SERVICE_TYPE_SP)) { + searchClient = spClient; + logger.info("Setting a Service Proxy client to serve requests."); + } else { + logger.info("Clearing search client. No client defined to serve requests at this point."); + searchClient = null; + } + } + + public SearchClient getSearchClient() { + return searchClient; + } }