I am using struts-2.3.16 and I have to suppress exceptions from Freemarker template globally in our application. This means that, instead of the yellow screen with the stacktrace from Freemarker, I have to forward to a global jsp that displays a generic message, so preventing the display of stacktraces to the user. For generic exceptions in struts we mapped a global-results in struts.xml, but it's not working for Freemarker exceptions.
So far I have implemented the solution from What are different ways to handle error in FreeMarker template?. So I created a CustomFreemarkerManager and a CustomTemplateExceptionHandler.
My CustomFreemarkerManager looks like this:
#Override
public void init(ServletContext servletContext) throws TemplateException {
super.config = super.createConfiguration(servletContext);
super.config.setTemplateExceptionHandler(new CustomTemplateExceptionHandler(servletContext));
super.contentType = "text/html";
super.wrapper = super.createObjectWrapper(servletContext);
if (LOG.isDebugEnabled()) {
LOG.debug("Using object wrapper of class " + super.wrapper.getClass().getName(), new String[0]);
}
super.config.setObjectWrapper(super.wrapper);
super.templatePath = servletContext.getInitParameter("TemplatePath");
if (super.templatePath == null) {
super.templatePath = servletContext.getInitParameter("templatePath");
}
super.configureTemplateLoader(super.createTemplateLoader(servletContext, super.templatePath));
super.loadSettings(servletContext);
}
#Override
protected Configuration createConfiguration(ServletContext servletContext) throws TemplateException {
Configuration configuration = new Configuration();
configuration.setTemplateExceptionHandler(new CustomTemplateExceptionHandler(servletContext));
if (super.mruMaxStrongSize > 0) {
configuration.setSetting("cache_storage", "strong:" + super.mruMaxStrongSize);
}
if (super.templateUpdateDelay != null) {
configuration.setSetting("template_update_delay", super.templateUpdateDelay);
}
if (super.encoding != null) {
configuration.setDefaultEncoding(super.encoding);
}
configuration.setLocalizedLookup(false);
configuration.setWhitespaceStripping(true);
return configuration;
}
From here I send the ServletContext to my CustomTemplateExceptionHandler so I can create a RequestDispatcher to forward to my exception.jsp. The problem is that in the exception handler I don't have the request and the response and I can't forward to my jsp.
The class CustomTemplateExceptionHandler looks like this so far:
private ServletContext servletContext;
public CustomTemplateExceptionHandler(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void handleTemplateException(TemplateException te, Environment env, Writer out) throws TemplateException {
if (servletContext != null) {
RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/resources/exception.jsp");
//HERE I have to forward to my jsp
}
}
Anybody knows how can I do that? I want the stacktrace to be logged only on the server, and in the UI to replace the stacktrace with a generic message.
Ok, so my solution for this problem was to print on the PrintWriter that comes in my CustomTemplateExceptionHandler a response similar with the standard HTML_DEBUG_HANDLER offered by Freemarker. Check out this link:
https://github.com/apache/incubator-freemarker/blob/2.3-gae/src/main/java/freemarker/template/TemplateExceptionHandler.java#L98
Here you can see how HTML_DEBUG_HANDLER is managed. I replaced printing the stacktrace with a general message. The Freemarker documentation advise you to use RETHROW_HANDLER and catch the exception later in your application after the call of Template.process(). See here:
http://freemarker.org/docs/app_faq.html#misc.faq.niceErrorPage
But because Struts2 is working with Freemarker backstage, and the Freemarker methods are executed after the action was executed, I couldn't figure it out how and where to catch the exception. I have managed to get the HttpServlet response and request in the method handleTemplateException() (see the question), but I could not forward to my exception.jsp because the response was already committed and so it was giving me an exception.
The final code looks like this:
Class CustomFreemarkerManager:
#Override
public void init(ServletContext servletContext) throws TemplateException {
super.config = super.createConfiguration(servletContext);
super.config.setTemplateExceptionHandler(new CustomTemplateExceptionHandler());
super.contentType = "text/html";
super.wrapper = super.createObjectWrapper(servletContext);
if (LOG.isDebugEnabled()) {
LOG.debug("Using object wrapper of class " + super.wrapper.getClass().getName(), new String[0]);
}
super.config.setObjectWrapper(super.wrapper);
super.templatePath = servletContext.getInitParameter("TemplatePath");
if (super.templatePath == null) {
super.templatePath = servletContext.getInitParameter("templatePath");
}
super.configureTemplateLoader(super.createTemplateLoader(servletContext, super.templatePath));
super.loadSettings(servletContext);
}
#Override
protected Configuration createConfiguration(ServletContext servletContext) throws TemplateException {
Configuration configuration = new Configuration();
configuration.setTemplateExceptionHandler(new CustomTemplateExceptionHandler());
if (super.mruMaxStrongSize > 0) {
configuration.setSetting("cache_storage", "strong:" + super.mruMaxStrongSize);
}
if (super.templateUpdateDelay != null) {
configuration.setSetting("template_update_delay", super.templateUpdateDelay);
}
if (super.encoding != null) {
configuration.setDefaultEncoding(super.encoding);
}
configuration.setLocalizedLookup(false);
configuration.setWhitespaceStripping(true);
return configuration;
}
Class CustomTemplateExceptionHandler:
public void handleTemplateException(TemplateException te, Environment env, Writer out) throws TemplateException {
boolean externalPw = out instanceof PrintWriter;
PrintWriter pw = externalPw ? (PrintWriter) out : new PrintWriter(out);
try {
pw.print("<!-- ERROR MESSAGE STARTS HERE -->"
+ "<!-- ]]> -->"
+ "</table></table></table>"
+ "<div align='left' style='"
+ "background-color:#FFFF7C; "
+ "display:block; "
+ "border-top:double; "
+ "padding:10px; "
+ "'>");
pw.print("<b style='"
+ "color: red; "
+ "font-size:14px; "
+ "font-style:normal; "
+ "font-weight:bold; "
+ "'>"
+ "Oops! We have encountered a problem. Please try again!"
+ "</b>");
pw.println("</div></html>");
pw.flush(); // To commit the HTTP response
} finally {
if (!externalPw) pw.close();
}
throw te;
}
If anyone finds a better response to this please post your answer!
It should be easier. If you don't want the yellow debug template error you have to switch the TemplateExceptionHandler from HTML_DEBUG_HANDLER to RETHROW_HANDLER (as the freemarker message on the top suggest: FreeMarker template error DEBUG mode; use RETHROW in production!)
Now, I prefer to do it programmatically (as you did) because I want to choose the TemplateExceptionHandler depending on the environment (PRO, TEST, DEVEL), my solution is to put environment information in the context.
public class CustomFreemarkerManager extends FreemarkerManager {
public CustomFreemarkerManager(){
super();
}
#Override
public void init(ServletContext servletContext) throws TemplateException {
//important!
super.init(servletContext);
//other stuff maybe you want to tune...
//Getting environmentInfo object from the context, it's a personal solution
EnvironmentInfo environmentInfo = (EnvironmentInfo)servletContext.getAttribute(EnvironmentInfo.CONTEXT_NAME);
if (environment.isPro()) {
config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
}else{
config.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
}
}
}
and tell struts to use your manager in struts.properties
struts.freemarker.manager.classname=com.jobisjob.northpole.web.core.CustomFreemarkerManager
Hope this helps.
Related
I use RestartResponseException in exception handler (IRequestCycleListener) for redirection on login page and it doesn't work. I see exception in logs but no changes in UI.
#Override
public IRequestHandler onException(RequestCycle cycle, Exception ex) {
if (couldNotLock) {
getSessionStore().invalidate(cycle.getRequest());
throw new RestartResponseException(LoginPage.class);
}
return null;
}
Even with your extra information in the question's comment it is still not clear what kind of problem you face.
I will try to guess: your LoginPage checks whether the user is already logged in and just redirects to the landing/home page of your application.
To fix that you need to invalidate the whole Session:
#Override
public IRequestHandler onException(RequestCycle cycle, Exception ex) {
if (couldNotLock) {
Session.get().invalidate(); // !!!
throw new RestartResponseException(LoginPage.class);
}
return null;
}
Try to throw new RestartResponseAtInterceptPageException(LoginPage.class) instead of RestartResponseException
If nothing else helps You can modify the page's URL with javascript.
#Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
response.render(JavaScriptHeaderItem.forScript("window.history.pushState(\"object or string\", \"" + pageTitle + "\", \"" + pageUrl + "\")"));
}
for AJAX:
AjaxRequestTarget target = optional.get();
target.prependJavaScript("window.history.pushState(\"object or string\", \"" + pageTitle + "\", \"" + pageUrl + "\")");
I am implementing a SOAP service in Mule ESB version 3.8 using the HTTP and CXF component. Please see attached image for the flow design.
The Mule flow is :
HTTP and CXF component exposes the web service which gives sum of two integers. The object send in request is :
public class AddValues{
private int a;
private int b;
public setA(int a)
{
this.a =a;
}
public getA()
{
return a;
}
public setB(int b)
{
this.b =b;
}
public getB()
{
return b;
}
}
Save the SOAP action using a variable.
Based on the SOAP Action route the Flow control.
Using a JAVA transformer to receive the payload and throw Custom Web fault exception as follows:
public class AddValuesBusinessLogic extends AbstractMessageTransformer
{
#Override
public Object transformMessage(MuleMessage message, String outputEncoding) throws TransformerException {
MuleMessage muleMessage = message;
AddValues addValues = (AddValues) muleMessage.getPayload();
if (addValues.getA() == null || addValues.getB() == null ) {
//Make an AddValueException object
throw new Exception("Add value exception");
}
return null;
}
}
But i am getting the error "Surround with try/catch"
My question is if I surround and handle the exception, how am I going to send the SOAP Fault to end user?
Can someone please suggest what is the best way to send a custom SOAP Fault from JAVA Transformer in Mule ESB?
I found a solution using TransformerException and CXF OutFaultInterceptor.
My approach is as follows:
Write a custom transformer class inside which add the validation rules. For example, if I want to throw Error for Integer a or b being null, I will add a Custom Transformer AddValuesBusinessLogic.class with the following code:
public class AddValuesBusinessLogic extends AbstractMessageTransformer
{
#Override
public Object transformMessage(MuleMessage message, String outputEncoding) throws
TransformerException
{
MuleMessage muleMessage = message;
AddValues addValues = (AddValues) muleMessage.getPayload();
if (addValues.getA() == null || addValues.getB() == null ) {
//Make an AddValueException object
throw new TransformerException(this,new AddValueException("BAD REQUEST"));
}
return "ALL OK";}
This exception will then propagate to CXF where I am writing an OutFaultInterceptor like follows:
public class AddValuesFaultInterceptor extends AbstractSoapInterceptor {
private static final Logger logger = LoggerFactory.getLogger(AddValuesFaultInterceptor.class);
public AddValuesFaultInterceptor() {
super(Phase.MARSHAL);
}
public void handleMessage(SoapMessage soapMessage) throws Fault {
Fault fault = (Fault) soapMessage.getContent(Exception.class);
if (fault.getCause() instanceof org.mule.api.transformer.TransformerMessagingException) {
Element detail = fault.getOrCreateDetail();
Element errorDetail = detail.getOwnerDocument().createElement("addValuesError");
Element errorCode = errorDetail.getOwnerDocument().createElement("errorCode");
Element message = errorDetail.getOwnerDocument().createElement("message");
errorCode.setTextContent("400");
message.setTextContent("BAD REQUEST");
errorDetail.appendChild(errorCode);
errorDetail.appendChild(message);
detail.appendChild(errorDetail);
}
}
private Throwable getOriginalCause(Throwable t) {
if (t instanceof ComponentException && t.getCause() != null) {
return t.getCause();
} else {
return t;
}
}
}
Now when I make a call using either SOAPUI or jaxws client, I get the custom fault exception in the SOAP response.
To extract the values of errorCode and errorMessage in JAXWS client I am doing the following in the catch block of try-catch:
catch (com.sun.xml.ws.fault.ServerSOAPFaultException soapFaultException) {
javax.xml.soap.SOAPFault fault = soapFaultException.getFault(); // <Fault> node
javax.xml.soap.Detail detail = fault.getDetail(); // <detail> node
java.util.Iterator detailEntries = detail.getDetailEntries(); // nodes under <detail>'
while(detailEntries.hasNext()) {
javax.xml.soap.DetailEntry detailEntry = (DetailEntry) detailEntries.next();
System.out.println(detailEntry.getFirstChild().getTextContent());
System.out.println(detailEntry.getLastChild().getTextContent());
}
}
This is working for me as of now.
However I will request suggestionsto improve on this workaound or if there are any better solutions.
Thanks everyone.
You should create your own SOAP Fault (plain Java String) and return it as a message. If you want, you can also create a transformer and put it in your catch-exception-strategy like this:
#Override
public Object transformMessage(MuleMessage message, String outputEncoding)
throws TransformerException {
String exceptionMessage = message.getExceptionPayload().getException().getCause().getMessage();
String outputMessage = "<soap:Fault xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"> " +
" <faultcode>soap:Server</faultcode> " +
"<faultstring>" + exceptionMessage + "</faultstring>" +
"</soap:Fault>";
return outputMessage;
}
Mule always expects Exceptions to be encapsulated in a TransformerException, so you should throw a new TransformerException setting your own Exception as the cause.
I have written some REST APIs using Java Servlets on Tomcat. These are my first experiences with Java and APIs and Tomcat. As I research and read about servlets, methods and parameter passing, and more recently thread safety, I realize I need some review, suggestions, and tutorial guidance from those of you who I see are far more experienced. I have found many questions / answers that seem to address pieces but my lack of experience clouds the clarity I desire.
The code below shows the top portion of one servlet example along with an example private method. I have "global" variables defined at the class level so that I may track the success of a method and determine if I need to send an error response. I do this because the method(s) already return a value.
Are those global variables creating an unsafe thread environment
Since the response is not visible in the private methods, how else might I determine the need to stop the process and send an error response if those global variables are unsafe
Though clipped for space, should I be doing all of the XML handling in the doGet method
Should I be calling all of the different private methods for the various data retrieval tasks and data handling
Should each method that accesses the same database open a Connection or should the doGet method create a Connection and pass it to each method
Assist, suggest, teach, guide to whatever you feel appropriate, or point me to the right learning resources so I may learn how to do better. Direct and constructive criticism welcome -- bashing and derogatory statements not preferred.
#WebServlet(name = "SubPlans", urlPatterns = {"*omitted*"})
public class SubPlans extends HttpServlet {
private transient ServletConfig servletConfig;
private String planSpecialNotes,
planAddlReqLinks,
legalTermsHeader,
legalTermsMemo,
httpReturnMsg;
private String[] subPlanInd = new String[4];
private boolean sc200;
private int httpReturnStatus;
private static final long serialVersionUID = 1L;
{
httpReturnStatus = 0;
httpReturnMsg = "";
sc200 = true;
planAddlReqLinks = null;
planSpecialNotes = null;
legalTermsHeader = "";
legalTermsMemo = null;
}
#Override
public void init(ServletConfig servletConfig)
throws ServletException {
this.servletConfig = servletConfig;
}
#Override
public ServletConfig getServletConfig() {
return servletConfig;
}
#Override
public String getServletInfo() {
return "SubPlans";
}
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<HashMap<String, Object>> alSubDeps = new ArrayList<HashMap<String, Object>>();
String[] coverageDates = new String[6],
depDates = new String[8];
String eeAltId = null,
eeSSN = null,
carrier = null,
logosite = null,
fmtSSN = "X",
subSQL = null,
healthPlan = null,
dentalPlan = null,
visionPlan = null,
lifePlan = null,
tier = null,
healthGroupNum = null,
effdate = null,
holdEffDate = null,
planDesc = "",
planYear = "",
summaryBenefitsLink = null;
int[][] effdates = new int[6][4];
int holdDistrictNumber = 0,
districtNumber = 0,
holdUnit = 0,
unit = 0;
boolean districtHasHSA = false;
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
try {
eeAltId = request.getParameter("*omitted*");
if ( eeAltId != null ) {
Pattern p = Pattern.compile(*omitted*);
Matcher m = p.matcher(eeAltId);
if ( m.find(0) ) {
eeSSN = getSSN(eeAltId);
} else {
httpReturnStatus = 412;
httpReturnMsg = "Alternate ID format incorrect.";
System.err.println("Bad alternate id format " + eeAltId);
sc200 = false;
}
} else {
httpReturnStatus = 412;
httpReturnMsg = "Alternate ID missing.";
System.err.println("alternate id not provided.");
sc200 = false;
}
if ( sc200 ) {
coverageDates = determineDates();
subSQL = buildSubSQLStatement(eeSSN, coverageDates);
alSubDeps = getSubDeps(subSQL);
if ( sc200 ) {
XMLStreamWriter writer = outputFactory.createXMLStreamWriter(response.getOutputStream());
writer.writeStartDocument("1.0");
writer.writeStartElement("subscriber");
// CLIPPED //
writer.writeEndElement(); // subscriber
writer.writeEndDocument();
if ( sc200 ) {
response.setStatus(HttpServletResponse.SC_OK);
writer.flush();
} else {
response.sendError(httpReturnStatus, httpReturnMsg);
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("Error writing XML");
System.err.println(e);
}
}
#Override
public void destroy() {
}
private String getPlanDescription(String planID) {
String planDesc = null;
String sqlEE = "SELECT ...";
Connection connGPD = null;
Statement stGPD = null;
ResultSet rsGPD = null;
try {
connGPD = getDbConnectionEE();
try {
stGPD = connGPD.createStatement();
planDesc = "Statement error";
try {
rsGPD = stGPD.executeQuery(sqlEE);
if ( !rsGPD.isBeforeFirst() )
planDesc = "No data";
else {
rsGPD.next();
planDesc = rsGPD.getString("Plan_Description");
}
} catch (Exception rsErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error retrieving plan description.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(rsErr);
sc200 = false;
} finally {
if ( rsGPD != null ) {
try {
rsGPD.close();
} catch (Exception rsErr) {
System.err.println("getPlanDescription: Error closing result set.");
System.err.println(rsErr);
}
}
}
} catch (Exception stErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error creating plan description statement.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(stErr);
sc200 = false;
} finally {
if ( stGPD != null ) {
try {
stGPD.close();
} catch (Exception stErr) {
System.err.println("getPlanDescription: Error closing query statement.");
System.err.println(stErr);
}
}
}
} catch (Exception connErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error closing database.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(connErr);
sc200 = false;
} finally {
if ( connGPD != null ) {
try {
connGPD.close();
} catch (Exception connErr) {
System.err.println("getPlanDescription: Error closing connection.");
System.err.println(connErr);
}
}
}
return planDesc.trim();
}
I have "global" variables defined at the class level
You have instance variables declared at the class level. There are no globals in Java.
so that I may track the success of a method and determine if I need to send an error response.
Poor technique.
I do this because the method(s) already return a value.
You should use exceptions for this if the return values are already taken.
Are those global variables creating an unsafe thread environment
Those instance variables are creating an unsafe thread environment.
Since the response is not visible in the private methods, how else might I determine the need to stop the process and send an error response if those global variables are unsafe?
Via exceptions thrown by the methods, see above. If there is no exception, send an OK response, whatever form that takes, otherwise whatever error response is appropriate to the exception.
Though clipped for space, should I be doing all of the XML handling in the doGet method
Not if it's long or repetitive (used in other places too).
Should I be calling all of the different private methods for the various data retrieval tasks and data handling
Sure, why not?
Should each method that accesses the same database open a Connection or should the doGet() method create a Connection and pass it to each method
doGet() should open the connection, pass it to each method, and infallibly close it.
NB You don't need the ServletConfig variable, or the init() or getServletConfig() methods. If you remove all this you can get it from the base class any time you need it via the getServletConfig() method you have pointlessly overridden.
The variables you have defined are instance members. They are not global and are not class-level. They are variables scoped to one instance of your servlet class.
The servlet container typically creates one instance of your servlet and sends all requests to that one instance. So you will have concurrent requests overwriting these variables’ contents unpredictably.
It can be ok for a servlet to have static variables or instance member variables, but only if their contents are thread safe and they contain no state specific to a request. For instance it would be normal to have a (log4j or java.util.logging) Logger object as a static member, where the logger is specifically designed to be called concurrently without the threads interfering with each other.
For error handling use exceptions to fail fast once something goes wrong.
Servlets are painful to write and hard to test. Consider using a MVC web framework instead. Frameworks like spring or dropwizard provide built-in capabilities that make things like data access and error handling easier, but most importantly they encourage patterns where you write separate well-focused classes that each do one thing well (and can be reasoned about and tested independently). The servlet approach tends to lead people to cram disparate functions into one increasingly-unmanageable class file, which seems to be the road you’re headed down.
I'm trying to extend my library for integrating Swing and JPA by making JPA config as automatic (and portable) as can be done, and it means programmatically adding <class> elements. (I know it can be done via Hibernate's AnnotationConfiguration or EclipseLInk's ServerSession, but - portability). I'd also like to avoid using Spring just for this single purpose.
I can create a persistence.xml on the fly, and fill it with <class> elements from specified packages (via the Reflections library). The problem starts when I try to feed this persistence.xml to a JPA provider. The only way I can think of is setting up a URLClassLoader, but I can't think of a way what wouldn't make me write the file to the disk somewhere first, for sole ability to obtain a valid URL. Setting up a socket for serving the file via an URL(localhost:xxxx) seems... I don't know, evil?
Does anyone have an idea how I could solve this problem? I know it sounds like a lot of work to avoid using one library, but I'd just like to know if it can be done.
EDIT (a try at being more clear):
Dynamically generated XML is kept in a String object. I don't know how to make it available to a persistence provider. Also, I want to avoid writing the file to disk.
For purpose of my problem, a persistence provider is just a class which scans the classpath for META-INF/persistence.xml. Some implementations can be made to accept dynamic creation of XML, but there is no common interface (especially for a crucial part of the file, the <class> tags).
My idea is to set up a custom ClassLoader - if you have any other I'd be grateful, I'm not set on this one.
The only easily extendable/configurable one I could find was a URLClassLoader. It works on URL objects, and I don't know if I can create one without actually writing XML to disk first.
That's how I'm setting things up, but it's working by writing the persistenceXmlFile = new File("META-INF/persistence.xml") to disk:
Thread.currentThread().setContextClassLoader(
new URLResourceClassLoader(
new URL[] { persistenceXmlFile.toURI().toURL() },
Thread.currentThread().getContextClassLoader()
)
);
URLResourceClassLoader is URLCLassLoader's subclass, which allows for looking up resources as well as classes, by overriding public Enumeration<URL> findResources(String name).
Maybe a bit late (after 4 years), but for others that are looking for a similar solution, you may be able to use the URL factory I created:
public class InMemoryURLFactory {
public static void main(String... args) throws Exception {
URL url = InMemoryURLFactory.getInstance().build("/this/is/a/test.txt", "This is a test!");
byte[] data = IOUtils.toByteArray(url.openConnection().getInputStream());
// Prints out: This is a test!
System.out.println(new String(data));
}
private final Map<URL, byte[]> contents = new WeakHashMap<>();
private final URLStreamHandler handler = new InMemoryStreamHandler();
private static InMemoryURLFactory instance = null;
public static synchronized InMemoryURLFactory getInstance() {
if(instance == null)
instance = new InMemoryURLFactory();
return instance;
}
private InMemoryURLFactory() {
}
public URL build(String path, String data) {
try {
return build(path, data.getBytes("UTF-8"));
} catch (UnsupportedEncodingException ex) {
throw new RuntimeException(ex);
}
}
public URL build(String path, byte[] data) {
try {
URL url = new URL("memory", "", -1, path, handler);
contents.put(url, data);
return url;
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
}
private class InMemoryStreamHandler extends URLStreamHandler {
#Override
protected URLConnection openConnection(URL u) throws IOException {
if(!u.getProtocol().equals("memory")) {
throw new IOException("Cannot handle protocol: " + u.getProtocol());
}
return new URLConnection(u) {
private byte[] data = null;
#Override
public void connect() throws IOException {
initDataIfNeeded();
checkDataAvailability();
// Protected field from superclass
connected = true;
}
#Override
public long getContentLengthLong() {
initDataIfNeeded();
if(data == null)
return 0;
return data.length;
}
#Override
public InputStream getInputStream() throws IOException {
initDataIfNeeded();
checkDataAvailability();
return new ByteArrayInputStream(data);
}
private void initDataIfNeeded() {
if(data == null)
data = contents.get(u);
}
private void checkDataAvailability() throws IOException {
if(data == null)
throw new IOException("In-memory data cannot be found for: " + u.getPath());
}
};
}
}
}
We can use the Jimfs google library for that.
First, we need to add the maven dependency to our project:
<dependency>
<groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId>
<version>1.2</version>
</dependency>
After that, we need to configure our filesystem behavior, and write our String content to the in-memory file, like this:
public static final String INPUT =
"\n"
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<note>\n"
+ " <to>Tove</to>\n"
+ " <from>Jani</from>\n"
+ " <heading>Reminder</heading>\n"
+ " <body>Don't forget me this weekend!</body>\n"
+ "</note>";
#Test
void usingJIMFS() throws IOException {
try (var fs = Jimfs.newFileSystem(Configuration.unix())) {
var path = fs.getPath(UUID.randomUUID().toString());
Files.writeString(path, INPUT);
var url = path.toUri().toURL();
assertThat(url.getProtocol()).isEqualTo("jimfs");
assertThat(Resources.asCharSource(url, UTF_8).read()).isEqualTo(INPUT);
}
}
We can find more examples in the official repository.
If we look inside the jimfs source code we will find the implementation is similar to #NSV answer.
I had a doubt regarding Messages being set in the custom validator in JSF 2.0.
here is the code
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
matcher = pattern.matcher(value.toString());
if(!matcher.matches()){
FacesMessage msg =
new FacesMessage("E-mail validation failed.",
"Invalid E-mail format.");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
}
}
Now here I am curious about how is it actually working, since we haven't written the part of the code
context.add(msg)
SO without adding these messages in the FacesContext how does it manage to retrieve it on the UI???
JSF does that in the UIInput#validateValue() method. It just calls your validator inside a try-catch block, catches the ValidatorException, extracts the FacesMessage from it and finally adds it to the FacesContext on component's client ID.
JSF implementations are open source. In Mojarra 2.1.3 source code you can find the following starting at line 1143 of the UIInput class:
// If our value is valid and not empty or empty w/ validate empty fields enabled, call all validators
if (isValid() && (!isEmpty(newValue) || validateEmptyFields(context))) {
if (validators != null) {
Validator[] validators = this.validators.asArray(Validator.class);
for (Validator validator : validators) {
try {
validator.validate(context, this, newValue);
}
catch (ValidatorException ve) {
// If the validator throws an exception, we're
// invalid, and we need to add a message
setValid(false);
FacesMessage message;
String validatorMessageString = getValidatorMessage();
if (null != validatorMessageString) {
message =
new FacesMessage(FacesMessage.SEVERITY_ERROR,
validatorMessageString,
validatorMessageString);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
} else {
Collection<FacesMessage> messages = ve.getFacesMessages();
if (null != messages) {
message = null;
String cid = getClientId(context);
for (FacesMessage m : messages) {
/* Here --> */ context.addMessage(cid, m);
}
} else {
message = ve.getFacesMessage();
}
}
if (message != null) {
/* And here --> */ context.addMessage(getClientId(context), message);
}
}
}
}
}
jsf has a catch exception in back and he is retrieving your message..
If this doesn't answer your question I don't understand your question...
maybe this link will help you:
http://java.dzone.com/articles/jsf-validation-tutorial-error
You have to set a place for errors in jsf as well..