<h:form id="activeclientsForm" prependId="false">\r
<h:outputText id="activeClientsLabel" value="Active clients: " style="${cc.attrs.debug == 'true' ? '' : 'display:none;'}"/> \r
<h:outputText id="activeclientsField" value="${pz2.update()}" style="${cc.attrs.debug == 'true' ? '' : 'display:none;'}"/>\r
- <h:panelGrid id="errorMessages" style="${pz2.hasErrors() ? 'color: red;' : 'display: none;'}">\r
- <p><h:outputText id="messages" value="Error: #{pz2.firstErrorMessage}"/></p>\r
+ <h:panelGrid id="errorMessages" style="${pz2.hasErrors() ? 'display: block;' : 'display: none;'}">\r
+ <p><h:outputText id="messages" value="Error: #{pz2.oneError.errorMessage}" style="color: red;"/></p>\r
+ <h:dataTable value="#{pz2.oneError.suggestions}" var="suggestion" cellspacing="0" cellpadding="0"> \r
+ <h:column>\r
+ #{suggestion} \r
+ </h:column> \r
+ </h:dataTable> \r
</h:panelGrid> \r
</h:form>\r
\r
\r
import java.io.IOException;\r
import java.io.Serializable;\r
+import java.util.List;\r
\r
public interface Pz2Configurator extends Serializable {\r
public Pz2Config getConfig() throws IOException;\r
+ \r
+ public List<String> document();\r
}\r
package com.indexdata.pz2utils4jsf.config;\r
\r
import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
\r
import javax.enterprise.context.SessionScoped;\r
import javax.enterprise.inject.Alternative;\r
import com.indexdata.masterkey.config.ModuleConfiguration;\r
import com.indexdata.pz2utils4jsf.utils.Utils;\r
\r
+import static com.indexdata.pz2utils4jsf.utils.Utils.nl;\r
+\r
@Named @SessionScoped @Alternative\r
public class Pz2ConfigureByMk2Config implements Pz2Configurator {\r
\r
"pazpar-application-jsf", ((HttpServletRequest) externalContext.getRequest()).getServerName());\r
ModuleConfiguration moduleConfig = mkConfigContext.getModuleConfiguration("pz2client");\r
pz2config = new Pz2Config(moduleConfig);\r
- logger.info("Accessing Pazpar2 at: " +pz2config.get("PAZPAR2_URL"));\r
+ logger.info(document());\r
+ }\r
+ \r
+\r
+ public List<String> document() {\r
+ List<String> doc = new ArrayList<String>();\r
+ \r
+ doc.add("Set to access Pazpar2 at: " +pz2config.get("PAZPAR2_URL"));\r
if (pz2config.get("PAZPAR2_SERVICE_XML") != null) {\r
- logger.info("Using the service definition contained in " + pz2config.getConfigFilePath() + "/" + pz2config.get("PAZPAR2_SERVICE_XML"));\r
+ doc.add("Set to use the service definition contained in " + pz2config.getConfigFilePath() + "/" + pz2config.get("PAZPAR2_SERVICE_XML"));\r
if (pz2config.get("PAZPAR2_SETTINGS_XML") != null) {\r
- logger.info("Using the target settings contained in " + pz2config.getConfigFilePath() + "/" + pz2config.get("PAZPAR2_SETTINGS_XML"));\r
+ doc.add("Set to use the target settings contained in " + pz2config.getConfigFilePath() + "/" + pz2config.get("PAZPAR2_SETTINGS_XML"));\r
} else {\r
- logger.info("Using the server side target settings as defined in the service definition.");\r
+ doc.add("Set to use the server side target settings as defined in the service definition.");\r
}\r
} else if (pz2config.get("PAZPAR2_SERVICE_ID") != null) {\r
- logger.info("Using the server side service definition identified by service id "+pz2config.get("PAZPAR2_SERVICE_ID"));\r
+ doc.add("Set to use the server side service definition identified by service id "+pz2config.get("PAZPAR2_SERVICE_ID"));\r
} else {\r
- logger.error("Did not find service ID nor service definition XML file so set up pazpar2 service.");\r
+ doc.add("Error: Did not find service ID nor service definition XML file to set up pazpar2 service.");\r
}\r
-\r
+ return doc;\r
}\r
- \r
+ \r
\r
}\r
package com.indexdata.pz2utils4jsf.config;\r
\r
import java.io.IOException;\r
+import java.util.ArrayList;\r
import java.util.HashMap;\r
+import java.util.List;\r
import java.util.Map;\r
\r
import javax.enterprise.context.SessionScoped;\r
parameters.put("PAZPAR2_SERVICE_ID", servletContext.getInitParameter("PAZPAR2_SERVICE_ID"));\r
pz2config = new Pz2Config(parameters); \r
}\r
+ \r
+ public List<String> document() {\r
+ List<String> doc = new ArrayList<String>();\r
+ doc.add("No documentation written yet for this configurator");\r
+ return doc;\r
+ }\r
}\r
--- /dev/null
+package com.indexdata.pz2utils4jsf.pazpar2;\r
+\r
+import java.util.ArrayList;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import org.apache.log4j.Logger;\r
+\r
+import com.indexdata.pz2utils4jsf.config.Pz2Configurator;\r
+import com.indexdata.pz2utils4jsf.utils.Utils;\r
+import static com.indexdata.pz2utils4jsf.utils.Utils.nl;\r
+\r
+public class ApplicationTroubleshooter {\r
+\r
+ private static Pattern httpResponsePattern = Pattern.compile("Unexpected HTTP response code \\(([0-9]*)\\).*");\r
+ private static Logger logger = Logger.getLogger(ApplicationTroubleshooter.class);\r
+ \r
+ private Pz2Configurator configurator = null;\r
+ \r
+ public ApplicationTroubleshooter(Pz2Configurator configurator) {\r
+ this.configurator = configurator;\r
+ }\r
+ \r
+ public ArrayList<String> getSuggestions(String commandName, String errorMessage) {\r
+ ArrayList<String> suggestions = new ArrayList<String>();\r
+ if (errorMessage.startsWith("Unexpected HTTP response")) {\r
+ Matcher m = httpResponsePattern.matcher(errorMessage);\r
+ if (m.matches()) {\r
+ String errorCode = m.group(1);\r
+ if (errorCode.equals("404")) {\r
+ suggestions.add("Pazpar2 service not found (response code 404). ");\r
+ suggestions.add("Please check the PAZPAR2_URL configuration and verify " +\r
+ "that a pazpar2 service is running at the given address."); \r
+ suggestions.add("The application was configured using " + Utils.baseObjectName(configurator));\r
+ suggestions.add("The configurator reports following configuration was used: ");\r
+ suggestions.addAll(configurator.document()); \r
+ } else {\r
+ suggestions.add("Response code was " + errorCode + ". " + nl +\r
+ "Please check the PAZPAR2_URL configuration and verify " + \r
+ "that a pazpar2 service is running at the given address." + nl); \r
+ } \r
+ } else {\r
+ logger.warn("Found message but no pattern match"); \r
+ } \r
+ }\r
+ if (errorMessage == null || errorMessage.length()==0) {\r
+ logger.debug("No error message found, no suggestions made.");\r
+ } else {\r
+ logger.info("No suggestions yet for message " + errorMessage);\r
+ }\r
+ return suggestions;\r
+ }\r
+}\r
\r
import org.apache.log4j.Logger;\r
\r
-import com.indexdata.pz2utils4jsf.pazpar2.CommandThread;\r
-import com.indexdata.pz2utils4jsf.pazpar2.Pazpar2Command;\r
-import com.indexdata.utils.XmlUtils;\r
import com.indexdata.masterkey.pazpar2.client.ClientCommand;\r
import com.indexdata.masterkey.pazpar2.client.Pazpar2Client;\r
import com.indexdata.masterkey.pazpar2.client.exceptions.Pazpar2ErrorException;\r
+import com.indexdata.pz2utils4jsf.pazpar2.data.ApplicationError;\r
\r
public class CommandThread extends Thread {\r
\r
this.client = client;\r
}\r
\r
+ /**\r
+ * Runs the specified command using the specified Pazpar2 client\r
+ * Sets the Pazpar2 response as an XML response string to be retrieved by\r
+ * getResponse().\r
+ * \r
+ * In case of an exception, an error response is generated, the document\r
+ * element being the same as it would have been if successful (named after\r
+ * the command, that is). \r
+ * \r
+ */\r
public void run() {\r
ClientCommand clientCommand = new ClientCommand(command.getName(), command.getEncodedQueryString());\r
if (command.getName().equals("search")) {\r
long end = System.currentTimeMillis(); \r
logger.debug("Executed " + command.getName() + " in " + (end-start) + " ms." );\r
} catch (IOException e) {\r
- logger.error("Message: " + e.getMessage()); \r
- response = new StringBuilder("<"+command.getName()+"><error exception=\"io\">"+XmlUtils.escape(e.getMessage())+"</error></"+command.getName()+">");\r
+ response.append(ApplicationError.createErrorXml(command.getName(), "io", e.getMessage())); \r
logger.error(response.toString());\r
} catch (Pazpar2ErrorException e) {\r
- logger.error(e.getMessage());\r
- response = new StringBuilder("<"+command.getName()+"><error exception=\"pazpar2error\">"+XmlUtils.escape(e.getMessage())+"</error></"+command.getName()+">"); \r
+ response.append(ApplicationError.createErrorXml(command.getName(), "pazpar2error", e.getMessage())); \r
logger.error(response.toString());\r
}\r
}\r
\r
+ /**\r
+ * \r
+ * @return Pazpar2 response as an XML string, possibly a generated error XML\r
+ */\r
public String getResponse () {\r
return response.toString();\r
}\r
- \r
+ \r
public Pazpar2Command getCommand() {\r
return command;\r
}\r
\r
import com.indexdata.pz2utils4jsf.config.Pz2Configurator;\r
import com.indexdata.pz2utils4jsf.controls.ResultsPager;\r
+import com.indexdata.pz2utils4jsf.pazpar2.data.ApplicationError;\r
import com.indexdata.pz2utils4jsf.pazpar2.data.ByTarget;\r
import com.indexdata.pz2utils4jsf.pazpar2.data.RecordResponse;\r
import com.indexdata.pz2utils4jsf.pazpar2.data.ShowResponse;\r
public boolean hasErrors() {\r
return pz2.hasErrors();\r
}\r
- \r
- public String getErrorMessages() {\r
- return pz2.getErrorMessages();\r
- }\r
- \r
- public String getFirstErrorMessage() {\r
- return pz2.getFirstErrorMessage();\r
+ \r
+ public ApplicationError getOneError() {\r
+ return pz2.getOneError();\r
}\r
\r
\r
\r
import com.indexdata.pz2utils4jsf.controls.ResultsPager;\r
import com.indexdata.pz2utils4jsf.pazpar2.TargetFilter;\r
+import com.indexdata.pz2utils4jsf.pazpar2.data.ApplicationError;\r
import com.indexdata.pz2utils4jsf.pazpar2.data.ByTarget;\r
import com.indexdata.pz2utils4jsf.pazpar2.data.RecordResponse;\r
import com.indexdata.pz2utils4jsf.pazpar2.data.ShowResponse;\r
public void setCurrentStateKey(String key);\r
\r
public boolean hasErrors();\r
- \r
- public String getErrorMessages();\r
- \r
- public String getFirstErrorMessage();\r
+ \r
+ public ApplicationError getOneError();\r
\r
}\r
import com.indexdata.masterkey.pazpar2.client.exceptions.ProxyErrorException;\r
import com.indexdata.pz2utils4jsf.config.Pz2Configurator;\r
import com.indexdata.pz2utils4jsf.controls.ResultsPager;\r
+import com.indexdata.pz2utils4jsf.pazpar2.data.ApplicationError;\r
import com.indexdata.pz2utils4jsf.pazpar2.data.ByTarget;\r
import com.indexdata.pz2utils4jsf.pazpar2.data.Pazpar2ResponseData;\r
import com.indexdata.pz2utils4jsf.pazpar2.data.Pazpar2ResponseParser;\r
private com.indexdata.masterkey.pazpar2.client.Pazpar2Client client = null; \r
private TargetFilter targetFilter = null; \r
private ResultsPager pager = null; \r
- \r
+ private ApplicationTroubleshooter errorHelper = null;\r
+ \r
public Pz2Session () {\r
logger.info("Instantiating pz2 session object [" + Utils.objectId(this) + "]"); \r
}\r
public void init(Pz2Configurator pz2conf) {\r
if (client==null) {\r
logger.info(Utils.objectId(this) + " is configuring itself using the provided " + Utils.objectId(pz2conf));\r
- try {\r
- cfg = new com.indexdata.masterkey.pazpar2.client.Pazpar2ClientConfiguration(pz2conf.getConfig());\r
- client = new com.indexdata.masterkey.pazpar2.client.Pazpar2ClientGeneric(cfg);\r
- resetDataObjects();\r
- } catch (ProxyErrorException e) {\r
- e.printStackTrace();\r
- } catch (IOException ioe) {\r
- ioe.printStackTrace();\r
- }\r
+ try {\r
+ cfg = new com.indexdata.masterkey.pazpar2.client.Pazpar2ClientConfiguration(pz2conf.getConfig());\r
+ client = new com.indexdata.masterkey.pazpar2.client.Pazpar2ClientGeneric(cfg);\r
+ errorHelper = new ApplicationTroubleshooter(pz2conf); \r
+ resetDataObjects();\r
+ } catch (ProxyErrorException e) {\r
+ e.printStackTrace();\r
+ } catch (IOException ioe) {\r
+ ioe.printStackTrace();\r
+ }\r
} else {\r
logger.warn("Attempt to configure session but it already has a configured client");\r
}\r
}\r
}\r
for (CommandThread thread : threadList) {\r
- dataObjects.put(thread.getCommand().getName(), new Pazpar2ResponseParser().getObject(thread.getResponse())); \r
+ String commandName = thread.getCommand().getName();\r
+ Pazpar2ResponseData responseObject = Pazpar2ResponseParser.getParser().getDataObject(thread.getResponse());\r
+ dataObjects.put(commandName, responseObject); \r
}\r
return getActiveClients();\r
} else {\r
queryStates.setCurrentStateKey(key);\r
}\r
\r
+ /**\r
+ * Returns true if application error found in any response data objects \r
+ */\r
public boolean hasErrors () {\r
- if (dataObjects.get("search").isError()) {\r
+ if (dataObjects.get("search").hasApplicationError()) {\r
logger.info("Error detected in search");\r
return true;\r
}\r
for (String name : dataObjects.keySet()) {\r
- if (dataObjects.get(name).isError()) {\r
+ if (dataObjects.get(name).hasApplicationError()) {\r
logger.info("Error detected in " + name);\r
return true;\r
}\r
} \r
return false;\r
}\r
- \r
- public String getErrorMessages() {\r
- StringBuilder msgs = new StringBuilder("");\r
- for (String name : dataObjects.keySet()) { \r
- if (dataObjects.get(name).isError()) { \r
- msgs.append(name + ": " + dataObjects.get(name).getErrorMessage());\r
- } \r
- }\r
- return msgs.toString();\r
- }\r
+\r
\r
- public String getFirstErrorMessage() {\r
- if (dataObjects.get("search").isError()) {\r
- return "Error doing search: " + dataObjects.get("search").getErrorMessage();\r
- }\r
- for (String name : dataObjects.keySet()) { \r
- if (dataObjects.get(name).isError()) { \r
- return name + ": " + dataObjects.get(name).getErrorMessage(); \r
- } \r
+ /**\r
+ * Returns a search command error, if any, otherwise the first\r
+ * error found for an arbitrary command, if any, otherwise\r
+ * an empty dummy error. \r
+ */ \r
+ public ApplicationError getOneError() {\r
+ ApplicationError error = new ApplicationError(); \r
+ if (dataObjects.get("search").hasApplicationError()) {\r
+ error = dataObjects.get("search").getApplicationError(); \r
+ } else {\r
+ for (String name : dataObjects.keySet()) { \r
+ if (dataObjects.get(name).hasApplicationError()) { \r
+ error = dataObjects.get(name).getApplicationError(); \r
+ break;\r
+ } \r
+ }\r
}\r
- return "";\r
- \r
+ error.setTroubleshooter(errorHelper);\r
+ return error; \r
}\r
\r
\r
return pager;\r
}\r
\r
+ protected ApplicationTroubleshooter getTroubleshooter() {\r
+ return errorHelper;\r
+ }\r
+ \r
private void handleQueryStateChanges (String commands) {\r
if (queryStates.hasPendingStateChange("search")) { \r
logger.debug("Found pending search change. Doing search before updating " + commands);\r
--- /dev/null
+package com.indexdata.pz2utils4jsf.pazpar2.data;\r
+\r
+import static com.indexdata.pz2utils4jsf.utils.Utils.nl;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import com.indexdata.pz2utils4jsf.pazpar2.ApplicationTroubleshooter;\r
+import com.indexdata.utils.XmlUtils;\r
+\r
+public class ApplicationError extends Pazpar2ResponseData {\r
+\r
+ private static final long serialVersionUID = 8878776025779714122L;\r
+ private ApplicationTroubleshooter errorHelper = null;\r
+ \r
+ \r
+ public ApplicationError () { \r
+ }\r
+ \r
+ public String getCommandName() {\r
+ return getOneElementValue("commandname");\r
+ }\r
+ \r
+ public String getErrorMessage() {\r
+ return getOneElementValue("errormessage");\r
+ }\r
+ \r
+ public String getException () {\r
+ return getOneElementValue("exception");\r
+ }\r
+ \r
+ public List<String> getSuggestions() { \r
+ if (errorHelper!=null) {\r
+ return errorHelper.getSuggestions(getCommandName(), getErrorMessage());\r
+ } else {\r
+ List<String> nohelper = new ArrayList<String>();\r
+ nohelper.add("Tips: could not generate tips due to a programming error, error helper was not set");\r
+ return nohelper;\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Creates an XML string error message, embedded in an XML string document named by the command\r
+ * @param commandName\r
+ * @param exceptionName\r
+ * @param errorMessage\r
+ * @return\r
+ */\r
+ public static String createErrorXml (String commandName, String exceptionName, String errorMessage) {\r
+ StringBuilder errorXml = new StringBuilder("");\r
+ errorXml.append("<" + commandName + ">"+nl);\r
+ errorXml.append(" <applicationerror>"+nl);\r
+ errorXml.append(" <commandname>" + commandName + "</commandname>");\r
+ errorXml.append(" <exception>" + XmlUtils.escape(exceptionName) + "</exception>"+nl); \r
+ errorXml.append(" <errormessage>" + XmlUtils.escape(errorMessage) + "</errormessage>"+nl);\r
+ errorXml.append(" </applicationerror>"+nl);\r
+ errorXml.append("</" + commandName + ">"+nl);\r
+ return errorXml.toString(); \r
+ }\r
+ \r
+ public void setTroubleshooter (ApplicationTroubleshooter errorHelper) {\r
+ this.errorHelper = errorHelper; \r
+ }\r
+\r
+}\r
+++ /dev/null
-package com.indexdata.pz2utils4jsf.pazpar2.data;\r
-\r
-public class Pazpar2ErrorResponse extends Pazpar2ResponseData {\r
-\r
- private static final long serialVersionUID = 5261091417784893149L;\r
-\r
-}\r
HashMap<String,String> attributes = new HashMap<String,String>();\r
HashMap<String,List<Pazpar2ResponseData>> elements = new HashMap<String,List<Pazpar2ResponseData>>();\r
String textContent = "";\r
- String errorText = null;\r
+ ApplicationError error = null;\r
\r
public void setType (String type) {\r
this.type = type;\r
}\r
}\r
\r
+ /**\r
+ * Returns the text content of the first element found with the given\r
+ * name\r
+ * @param name of the element \r
+ * @return text value, empty string if none found\r
+ */\r
public String getOneElementValue (String name) {\r
if (getOneElement(name)!=null && getOneElement(name).getValue().length()>0) {\r
return getOneElement(name).getValue();\r
}\r
}\r
\r
- public boolean isError () {\r
- return (getOneElement("error") != null); \r
+ public boolean hasApplicationError () {\r
+ return (getOneElement("applicationerror") != null); \r
}\r
\r
- public String getErrorMessage() {\r
- return getOneElementValue("error");\r
+ public ApplicationError getApplicationError() {\r
+ return (ApplicationError) getOneElement("applicationerror");\r
}\r
-\r
+ \r
+ \r
+ \r
\r
}\r
}\r
}\r
\r
+ public static Pazpar2ResponseParser getParser() {\r
+ return new Pazpar2ResponseParser();\r
+ }\r
+ \r
private void initSax() throws ParserConfigurationException, SAXException {\r
SAXParserFactory spf = SAXParserFactory.newInstance();\r
spf.setNamespaceAware(true);\r
xmlReader.setContentHandler(this); \r
}\r
\r
- public Pazpar2ResponseData getObject (String response) {\r
+ /**\r
+ * Parses a Pazpar2 XML response -- or an error response as XML -- and produces a \r
+ * Pazpar2ResponseData object, i.e. a 'show' object\r
+ * \r
+ * @param response XML response string from Pazpar2\r
+ * @return Response data object\r
+ */\r
+ public Pazpar2ResponseData getDataObject (String response) {\r
try {\r
xmlReader.parse(new InputSource(new ByteArrayInputStream(response.getBytes("UTF-8"))));\r
} catch (UnsupportedEncodingException e) {\r
currentElement = new RecordResponse();\r
} else if (localName.equals("search")) {\r
currentElement = new SearchResponse();\r
+ } else if (localName.equals("applicationerror")) {\r
+ currentElement = new ApplicationError();\r
} else {\r
currentElement = new Pazpar2ResponseData();\r
}\r
\r
public class Utils {\r
\r
+ public static String nl = System.getProperty("line.separator"); \r
+\r
public static String objectId(Object o) {\r
int lastdot = o.toString().lastIndexOf('.');\r
if (lastdot>-1 && lastdot+1<o.toString().length()) {\r
return o.toString();\r
}\r
}\r
-\r
+ \r
+ public static String baseObjectName(Object o) {\r
+ String objName = o.getClass().getName();\r
+ if (objName.contains("$")) {\r
+ return objectId(objName.substring(0,objName.indexOf("$"))); \r
+ } else {\r
+ return objectId(objName);\r
+ }\r
+ }\r
}\r