I'm trying to add a very simple file input to my webapp which I'm doing using JSF2.0 and RichFaces 3.3.3, the thing is I really dislike the richfaces fileInput component and I'm looking for something simpler (in terms of use and looks), so far I've found:
tomahawk's fileInput - but tomahawk only supports JSF1.2
trinidad fileInput - but trinidad for JSF2.0 is in alpha stage
primefaces - but of course they won't work with RichFaces 3.3.3 (only 4.0 which are in beta)
Are there any other options? I need something really simple like a normal html file input component.
You basically need to do two things:
Create a Filter which puts the multipart/form-data items in a custom map and replace the original request parameter map with it so that the normal request.getParameter() process keeps working.
Create a JSF 2.0 custom component which renders a input type="file" and which is aware of this custom map and can obtain the uploaded files from it.
#taher has already given a link where you could find insights and code snippets. The JSF 2.0 snippets should be reuseable. You yet have to modify the MultipartMap to use the good 'ol Apache Commons FileUpload API instead of the Servlet 3.0 API.
If I have time, I will by end of day rewrite it and post it here.
Update: I almost forgot you, I did a quick update to replace Servlet 3.0 API by Commons FileUpload API, it should work:
package net.balusc.http.multipart;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
public class MultipartMap extends HashMap<String, Object> {
// Constants ----------------------------------------------------------------------------------
private static final String ATTRIBUTE_NAME = "parts";
private static final String DEFAULT_ENCODING = "UTF-8";
private static final int DEFAULT_BUFFER_SIZE = 10240; // 10KB.
// Vars ---------------------------------------------------------------------------------------
private String encoding;
private String location;
// Constructors -------------------------------------------------------------------------------
/**
* Construct multipart map based on the given multipart request and file upload location. When
* the encoding is not specified in the given request, then it will default to <tt>UTF-8</tt>.
* #param multipartRequest The multipart request to construct the multipart map for.
* #param location The location to save uploaded files in.
* #throws ServletException If something fails at Servlet level.
* #throws IOException If something fails at I/O level.
*/
#SuppressWarnings("unchecked") // ServletFileUpload#parseRequest() isn't parameterized.
public MultipartMap(HttpServletRequest multipartRequest, String location)
throws ServletException, IOException
{
multipartRequest.setAttribute(ATTRIBUTE_NAME, this);
this.encoding = multipartRequest.getCharacterEncoding();
if (this.encoding == null) {
multipartRequest.setCharacterEncoding(this.encoding = DEFAULT_ENCODING);
}
this.location = location;
try {
List<FileItem> parts = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(multipartRequest);
for (FileItem part : parts) {
if (part.isFormField()) {
processFormField(part);
} else if (!part.getName().isEmpty()) {
processFileField(part);
}
}
} catch (FileUploadException e) {
throw new ServletException("Parsing multipart/form-data request failed.", e);
}
}
// Actions ------------------------------------------------------------------------------------
#Override
public Object get(Object key) {
Object value = super.get(key);
if (value instanceof String[]) {
String[] values = (String[]) value;
return values.length == 1 ? values[0] : Arrays.asList(values);
} else {
return value; // Can be File or null.
}
}
/**
* #see ServletRequest#getParameter(String)
* #throws IllegalArgumentException If this field is actually a File field.
*/
public String getParameter(String name) {
Object value = super.get(name);
if (value instanceof File) {
throw new IllegalArgumentException("This is a File field. Use #getFile() instead.");
}
String[] values = (String[]) value;
return values != null ? values[0] : null;
}
/**
* #see ServletRequest#getParameterValues(String)
* #throws IllegalArgumentException If this field is actually a File field.
*/
public String[] getParameterValues(String name) {
Object value = super.get(name);
if (value instanceof File) {
throw new IllegalArgumentException("This is a File field. Use #getFile() instead.");
}
return (String[]) value;
}
/**
* #see ServletRequest#getParameterNames()
*/
public Enumeration<String> getParameterNames() {
return Collections.enumeration(keySet());
}
/**
* #see ServletRequest#getParameterMap()
*/
public Map<String, String[]> getParameterMap() {
Map<String, String[]> map = new HashMap<String, String[]>();
for (Entry<String, Object> entry : entrySet()) {
Object value = entry.getValue();
if (value instanceof String[]) {
map.put(entry.getKey(), (String[]) value);
} else {
map.put(entry.getKey(), new String[] { ((File) value).getName() });
}
}
return map;
}
/**
* Returns uploaded file associated with given request parameter name.
* #param name Request parameter name to return the associated uploaded file for.
* #return Uploaded file associated with given request parameter name.
* #throws IllegalArgumentException If this field is actually a Text field.
*/
public File getFile(String name) {
Object value = super.get(name);
if (value instanceof String[]) {
throw new IllegalArgumentException("This is a Text field. Use #getParameter() instead.");
}
return (File) value;
}
// Helpers ------------------------------------------------------------------------------------
/**
* Process given part as Text part.
*/
private void processFormField(FileItem part) {
String name = part.getFieldName();
String[] values = (String[]) super.get(name);
if (values == null) {
// Not in parameter map yet, so add as new value.
put(name, new String[] { part.getString() });
} else {
// Multiple field values, so add new value to existing array.
int length = values.length;
String[] newValues = new String[length + 1];
System.arraycopy(values, 0, newValues, 0, length);
newValues[length] = part.getString();
put(name, newValues);
}
}
/**
* Process given part as File part which is to be saved in temp dir with the given filename.
*/
private void processFileField(FileItem part) throws IOException {
// Get filename prefix (actual name) and suffix (extension).
String filename = FilenameUtils.getName(part.getName());
String prefix = filename;
String suffix = "";
if (filename.contains(".")) {
prefix = filename.substring(0, filename.lastIndexOf('.'));
suffix = filename.substring(filename.lastIndexOf('.'));
}
// Write uploaded file.
File file = File.createTempFile(prefix + "_", suffix, new File(location));
InputStream input = null;
OutputStream output = null;
try {
input = new BufferedInputStream(part.getInputStream(), DEFAULT_BUFFER_SIZE);
output = new BufferedOutputStream(new FileOutputStream(file), DEFAULT_BUFFER_SIZE);
IOUtils.copy(input, output);
} finally {
IOUtils.closeQuietly(output);
IOUtils.closeQuietly(input);
}
put(part.getFieldName(), file);
part.delete(); // Cleanup temporary storage.
}
}
You still need both the MultipartFilter and MultipartRequest classes as described in this article. You only need to remove the #WebFilter annotation and map the filter on an url-pattern of /* along with an <init-param> of location wherein you specify the absolute path where the uploaded files are to be stored. You can use the JSF 2.0 custom file upload component as described in this article unchanged.
Dear either you have to use rich:uploadFile or make custom component in JSF by following http://balusc.blogspot.com/2009/12/uploading-files-with-jsf-20-and-servlet.html
After I also tried tomahawk, I mentioned that it does not work with AJAX. So I decided to hack rich:fileUpload and perform the click on add button over a a4j:commandButton. Here's the code:
<a4j:form id="myForm">
<a4j:commandButton id="myButton" value="Upload" title="Upload" styleClass="myButtonClass"
onclick="document.getElementById('myForm:myFileUpload:file').click()/>
<rich:fileUpload id="myFileUpload" maxFilesQuantity="1" autoclear="true"
immediateUpload="true" styleClass="invisibleClass"
fileUploadListener="#{uploadBean.uploadListener}"/>
</a4j:form>
myForm:myFileUpload:file is the input-Element (type="file") for the Add-Button. invisibleClass should only contain display:none;. With style="display:none;" it won't work.
You can used rich faces 3.3.3 file upload.
Step 1 : fileUpload.xhtml
<rich:fileUpload id="fileupload" addControlLabel="Browse"
required="true"
fileUploadListener="#{testForm.listener}"
acceptedTypes="xml"
ontyperejected="alert('Only xml files are accepted');"
maxFilesQuantity="1" listHeight="57px" listWidth="100%"
disabled="#{testForm..disabled}" >
<a4j:support event="onclear"
action="#{testForm..clearUploadData}"
reRender="fileupload" />
</rich:fileUpload>
Step 2: FileUpload.java
public class FileUpload implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String Name;
private String mime;
private long length;
private byte [] file;
private String absolutePath;
public String getName() {
return Name;
}
/**
* #return the file
*/
public byte[] getFile() {
return file;
}
/**
* #param file the file to set
*/
public void setFile(byte[] file) {
this.file = file;
}
/**
* #param mime the mime to set
*/
public void setMime(String mime) {
this.mime = mime;
}
public void setName(String name) {
Name = name;
int extDot = name.lastIndexOf('.');
if(extDot > 0){
String extension = name.substring(extDot +1);
if("txt".equals(extension)){
mime="txt";
} else if("xml".equals(extension)){
mime="xml";
} else {
mime = "unknown file";
}
}
}
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
public String getMime(){
return mime;
}
/**
* #return the absolutePath
*/
public String getAbsolutePath() {
return absolutePath;
}
/**
* #param absolutePath the absolutePath to set
*/
public void setAbsolutePath(String absolutePath) {
this.absolutePath = absolutePath;
}
}
Step 3 :TestForm // calling listner
/**
*
* #param event
* #throws Exception
*/
public void listener(UploadEvent event) throws Exception{
UploadItem item = event.getUploadItem();
FileUpload file = new FileUpload();
file.setLength(item.getData().length);
file.setFile(item.getData());
file.setName(item.getFileName());
files.add(file);
}
Related
What is the way of reading a specific sheet from an Excel file using spring-batch-excel?
Specifically, I want to parse different sheets within an Excel file in a different manner, using a org.springframework.batch.item.excel.poi.PoiItemReader.
I can't see how to do this with the PoiItemReader, in that is appears to read each sheet in the document. Is there a way to handle sheets differently in the row mapper perhaps? Is it possible without writing a custom POI reader?
No way with out writing custom reader
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.springframework.batch.extensions.excel.Sheet;
import org.springframework.lang.Nullable;
public class PoiSheet implements Sheet {
private final DataFormatter dataFormatter = new DataFormatter();
private final org.apache.poi.ss.usermodel.Sheet delegate;
private final int numberOfRows;
private final String name;
private FormulaEvaluator evaluator;
/**
* Constructor which takes the delegate sheet.
* #param delegate the apache POI sheet
*/
PoiSheet(final org.apache.poi.ss.usermodel.Sheet delegate) {
super();
this.delegate = delegate;
this.numberOfRows = this.delegate.getLastRowNum() + 1;
this.name = this.delegate.getSheetName();
}
/**
* {#inheritDoc}
*/
#Override
public int getNumberOfRows() {
return this.numberOfRows;
}
/**
* {#inheritDoc}
*/
#Override
public String getName() {
return this.name;
}
/**
* {#inheritDoc}
*/
#Override
#Nullable
public String[] getRow(final int rowNumber) {
final Row row = this.delegate.getRow(rowNumber);
return map(row);
}
#Nullable
private String[] map(Row row) {
if (row == null) {
return null;
}
final List<String> cells = new LinkedList<>();
final int numberOfColumns = row.getLastCellNum();
for (int i = 0; i < numberOfColumns; i++) {
Cell cell = row.getCell(i);
CellType cellType = cell.getCellType();
if (cellType == CellType.FORMULA) {
cells.add(this.dataFormatter.formatCellValue(cell, getFormulaEvaluator()));
}
else {
cells.add(this.dataFormatter.formatCellValue(cell));
}
}
return cells.toArray(new String[0]);
}
/**
* Lazy getter for the {#code FormulaEvaluator}. Takes some time to create an
* instance, so if not necessary don't create it.
* #return the {#code FormulaEvaluator}
*/
private FormulaEvaluator getFormulaEvaluator() {
if (this.evaluator == null) {
this.evaluator = this.delegate.getWorkbook().getCreationHelper().createFormulaEvaluator();
}
return this.evaluator;
}
#Override
public Iterator<String[]> iterator() {
return new Iterator<String[]>() {
private final Iterator<Row> delegateIter = PoiSheet.this.delegate.iterator();
#Override
public boolean hasNext() {
return this.delegateIter.hasNext();
}
#Override
public String[] next() {
return map(this.delegateIter.next());
}
};
}
}
Excel Reader
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.batch.extensions.excel.AbstractExcelItemReader;
import org.springframework.batch.extensions.excel.Sheet;
import org.springframework.core.io.Resource;
public class ExcelSheetItemReader <T> extends AbstractExcelItemReader<T> {
private Workbook workbook;
private InputStream inputStream;
private int sheetIndex = 0;
#Override
protected Sheet getSheet(final int sheet) {
return new PoiSheet(this.workbook.getSheetAt(sheetIndex));
}
#Override
protected int getNumberOfSheets() {
return 1;
}
#Override
protected void doClose() throws Exception {
super.doClose();
if (this.inputStream != null) {
this.inputStream.close();
this.inputStream = null;
}
if (this.workbook != null) {
this.workbook.close();
this.workbook = null;
}
}
/**
* Open the underlying file using the {#code WorkbookFactory}. Prefer {#code File}
* based access over an {#code InputStream}. Using a file will use fewer resources
* compared to an input stream. The latter will need to cache the whole sheet
* in-memory.
* #param resource the {#code Resource} pointing to the Excel file.
* #param password the password for opening the file
* #throws Exception is thrown for any errors.
*/
#Override
protected void openExcelFile(final Resource resource, String password) throws Exception {
try {
File file = resource.getFile();
this.workbook = WorkbookFactory.create(file, password, false);
}
catch (FileNotFoundException ex) {
this.inputStream = resource.getInputStream();
this.workbook = WorkbookFactory.create(this.inputStream, password);
}
this.workbook.setMissingCellPolicy(Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
}
public int getSheetIndex() {
return sheetIndex;
}
public void setSheetIndex(int sheetIndex) {
this.sheetIndex = sheetIndex;
}
}
Another example can be found here
I'm working into a method trying to change the default rest-assured log (which goes to the console) to a file using log4j.
It's a JUnit project which methods finally call to a REST facade, which have methods like this one.
private ResponseSpecification responseSpecification(RequestSpecification requestSpecification, Matcher matcher, int statusCode) {
ResponseSpecification responseSpecification = requestSpecification.expect().statusCode(StatusCode).body(".", is(matcher));
if (log) {
responseSpecification = responseSpecification.log().all();
}
return responseSpecification;
}
Following the official doc, I've changed the method like this:
private ResponseSpecification responseSpecification(RequestSpecification requestSpecification, Matcher matcher, int statusCode) {
final StringWriter writer = new StringWriter();
final PrintStream captor = new PrintStream(new WriterOutputStream(writer), true);
ResponseSpecification responseSpecification = requestSpecification.filter(logResponseTo(captor)).expect().statusCode(statusCode).body(".", is(matcher));
System.out.println("writer = " + writer.toString() + " <-");
return responseSpecification;
}
But writer.toString() prints always a void string (the old implementation works fine). Maybe I'm doing something wrong, but what? :(
I need to get something printable which can be managed by log4j, in this or other way.
Can anyone help me?
I've just solved the problem writing this into the RestSuite.setUp() method
RestAssured.config = config().logConfig(new LogConfig(defaultPrintStream));
and keeping intact the old code.
private ResponseSpecification responseSpecification(RequestSpecification requestSpecification, Matcher matcher, int statusCode) {
ResponseSpecification responseSpecification = requestSpecification.expect().statusCode(StatusCode).body(".", is(matcher));
if (log) {
responseSpecification = responseSpecification.log().all();
}
return responseSpecification;
}
I hope it can help to someone in a future.
Might be useful too: Here a class which redirects the restAssured log() calls to a supplied logger:
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import org.slf4j.Logger;
/**
* A wrapper class which takes a logger as constructor argument and offers a PrintStream whose flush
* method writes the written content to the supplied logger (debug level).
* <p>
* Usage:<br>
* initializing in #BeforeClass of the unit test:
* <pre>
* ToLoggerPrintStream loggerPrintStream = new ToLoggerPrintStream( myLog );
* RestAssured.config = RestAssured.config().logConfig(
* new LogConfig( loggerPrintStream.getPrintStream(), true ) );
* </pre>
* will redirect all log outputs of a ValidatableResponse to the supplied logger:
* <pre>
* resp.then().log().all( true );
* </pre>
*
* #version 1.0 (28.10.2015)
* #author Heri Bender
*/
public class ToLoggerPrintStream
{
/** Logger for this class */
private Logger myLog;
private PrintStream myPrintStream;
/**
* #return printStream
*/
public PrintStream getPrintStream()
{
if ( myPrintStream == null )
{
OutputStream output = new OutputStream()
{
private StringBuilder myStringBuilder = new StringBuilder();
#Override
public void write(int b) throws IOException
{
this.myStringBuilder.append((char) b );
}
/**
* #see java.io.OutputStream#flush()
*/
#Override
public void flush()
{
myLog.debug( this.myStringBuilder.toString() );
myStringBuilder = new StringBuilder();
}
};
myPrintStream = new PrintStream( output, true ); // true: autoflush must be set!
}
return myPrintStream;
}
/**
* Constructor
*
* #param aLogger
*/
public ToLoggerPrintStream( Logger aLogger )
{
super();
myLog = aLogger;
}
PrintStream fileOutPutStream = new PrintStream(new File("somefile.txt"));
RestAssured.config = config().logConfig(new LogConfig().defaultStream(fileOutPutStream));
Use a printStream to point to a file & give a filename you want to print the log.
Put the above code in your setup method for the tests & then just call log on your RequestSpecification instance as below
requestSpecification.log().all();
One possible modification to Heri's answer - instead of StringBuilder one can use ByteArrayOutputStream - that helps if one deals with multi-language data, e.g.
// [...]
PrintStream getPrintStream() {
if (printStream == null) {
OutputStream output = new OutputStream() {
ByteArrayOutputStream baos = new ByteArrayOutputStream()
#Override
public void write(int b) throws IOException {
baos.write(b)
}
#Override
public void flush() {
logger.debug(this.baos.toString())
baos = new ByteArrayOutputStream()
}
}
printStream = new PrintStream(output, true) // true: autoflush must be set!
}
return printStream
}
// [...]
I have the following commandButton action method handler:
public String reject()
{
//Do something
addMessage(null, "rejectAmountInvalid", FacesMessage.SEVERITY_ERROR);
redirectToPortlet("/xxx/inbox?source=pendingActions#pendingApproval");
}
public static void addMessage(String clientId, String key, Severity level, Object... objArr)
{
FacesContext context = FacesContext.getCurrentInstance();
FacesMessage message = null;
String msg = getTextFromResourceBundle(key);
if (objArr != null && objArr.length > 0)
msg = MessageFormat.format(msg, objArr);
message = new FacesMessage(msg);
message.setSeverity(level);
context.addMessage(clientId, message);
}
public static void redirectToPortlet(String urlToRedirect)
{
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
try
{
PortletRequest portletRequest = (PortletRequest) externalContext.getRequest();
ThemeDisplay themeDisplay = (ThemeDisplay) portletRequest.getAttribute("THEME_DISPLAY");
String portalURL = themeDisplay.getPortalURL();
String redirect = portalURL + urlToRedirect;
externalContext.redirect(redirect);
}
catch (Throwable e)
{
logger.log("Exception in redirectToPortlet to the URL: " + urlToRedirect, VLevel.ERROR, e);
}
}
When the page is redirected to "/xxx/inbox?source=pendingActions#pendingApproval", the error message I added is lost. Is there a way to preserve the error message in JSF 2.1?
Thanks
Sri
You can use a PhaseListener to save the messages that weren't displayed for the next request.
I've been using for a while one from Lincoln Baxter's blog post Persist and pass FacesMessages over multiple page redirects, you just copy the class to some package and register on your faces-config.xml.
It's not mentioned explicitly in the blog post, but I'm assuming the code is public domain, so I am posting here for a more self-contained answer:
package com.yoursite.jsf;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
/**
* Enables messages to be rendered on different pages from which they were set.
*
* After each phase where messages may be added, this moves the messages
* from the page-scoped FacesContext to the session-scoped session map.
*
* Before messages are rendered, this moves the messages from the
* session-scoped session map back to the page-scoped FacesContext.
*
* Only global messages, not associated with a particular component, are
* moved. Component messages cannot be rendered on pages other than the one on
* which they were added.
*
* To enable multi-page messages support, add a <code>lifecycle</code> block to your
* faces-config.xml file. That block should contain a single
* <code>phase-listener</code> block containing the fully-qualified classname
* of this file.
*
* #author Jesse Wilson jesse[AT]odel.on.ca
* #secondaryAuthor Lincoln Baxter III lincoln[AT]ocpsoft.com
*/
public class MultiPageMessagesSupport implements PhaseListener
{
private static final long serialVersionUID = 1250469273857785274L;
private static final String sessionToken = "MULTI_PAGE_MESSAGES_SUPPORT";
public PhaseId getPhaseId()
{
return PhaseId.ANY_PHASE;
}
/*
* Check to see if we are "naturally" in the RENDER_RESPONSE phase. If we
* have arrived here and the response is already complete, then the page is
* not going to show up: don't display messages yet.
*/
// TODO: Blog this (MultiPageMessagesSupport)
public void beforePhase(final PhaseEvent event)
{
FacesContext facesContext = event.getFacesContext();
this.saveMessages(facesContext);
if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
{
if (!facesContext.getResponseComplete())
{
this.restoreMessages(facesContext);
}
}
}
/*
* Save messages into the session after every phase.
*/
public void afterPhase(final PhaseEvent event)
{
if (!PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
{
FacesContext facesContext = event.getFacesContext();
this.saveMessages(facesContext);
}
}
#SuppressWarnings("unchecked")
private int saveMessages(final FacesContext facesContext)
{
List<FacesMessage> messages = new ArrayList<FacesMessage>();
for (Iterator<FacesMessage> iter = facesContext.getMessages(null); iter.hasNext();)
{
messages.add(iter.next());
iter.remove();
}
if (messages.size() == 0)
{
return 0;
}
Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
List<FacesMessage> existingMessages = (List<FacesMessage>) sessionMap.get(sessionToken);
if (existingMessages != null)
{
existingMessages.addAll(messages);
}
else
{
sessionMap.put(sessionToken, messages);
}
return messages.size();
}
#SuppressWarnings("unchecked")
private int restoreMessages(final FacesContext facesContext)
{
Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
List<FacesMessage> messages = (List<FacesMessage>) sessionMap.remove(sessionToken);
if (messages == null)
{
return 0;
}
int restoredCount = messages.size();
for (Object element : messages)
{
facesContext.addMessage(null, (FacesMessage) element);
}
return restoredCount;
}
}
And then, on your faces-config.xml:
<phase-listener>com.yoursite.jsf.MultiPageMessagesSupport</phase-listener>
If the redirect is to the same path, you could just use Flash#setKeepMessages().
context.getExternalContext().getFlash().setKeepMessages(true);
This way the messages are persisted in the flash scope which lives effectively as long as a single subsequent GET request (as occurs during a redirect).
I am using couchdb4j api to establish a connection through session object with couchdb(version 0.11.2).
Couchdb database is protected with username and password,
trying to establish connection with
Session(String host, int port, String user, String pass, boolean usesAuth, boolean secure) constructor by providing host,port,user,pass and usersAuth true, secure false,
but it is unable to establish a connection with username and password and giving a warning Authentication error: Unable to respond to any of these challenges: {}.
I used correct username and password.
Try a CouchDB lib that is actively maintained http://www.ektorp.org/ instead.
I solved the same issue by writing a simple Curl class that simplified the communication and return JSON in String form, since that is what is returned.
It should be self explanatory and you can read the main method for hints.
package path.to.the.class;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import Log;
/**
* This class takes on the simple task of doing http calls to any http web service like a web page or alike. Since the
* class is streamlined for JSON use this is the most easiest to set up.
*
* #author mikael.p.larsson#epsilon.nu
*
*/
public class Curl {
private Map<String, String> requestProperties = null;
private String charsetName = "UTF8";
public static final String GET = "GET";
public static final String PUT = "PUT";
public static final String POST = "POST";
public static final String HEAD = "HEAD";
public static final String DELETE = "DELETE";
/**
* This is the default constructor for Curl which takes it for granted that you want to communicate and read JSON.
* Most of the times this approach works even if plain html or text is requested.
*/
public Curl() {
requestProperties = new HashMap<String, String>();
requestProperties.put("Content-Type", "application/json");
}
/**
* With this alternate constructor a map containing header strings can be provided, useful if something apart from
* JSON is to be consumed.
*
* #param requestProperties
* a Map containing the header strings.
*/
public Curl(Map<String, String> requestProperties) {
this.requestProperties = requestProperties;
}
/**
* Public setter to enable setting charsetName.
*
* #param charsetName
* #return this instance to enable on liners.
*/
public Curl setCharsetName(String charsetName) {
this.charsetName = charsetName;
return this;
}
/**
* In the world of the web this is the command that a web browser does for you after you have entered an url into
* the address field. When using GET there should be no side effects on the site the data was requested from; the
* get method only consumes data and sends nothing.
*
* #param urlAsString
* the url to fetch from the internet.
* #return The response from the server
*/
public String get(String urlAsString) {
return doHttpCall(urlAsString, GET, null);
}
/**
* Put should be used when a resource should be sent to a server.
*
* #param urlAsString
* the url to the resource.
* #param doc
* the content to put
* #return The response from the server.
*/
public String put(String urlAsString, String doc) {
return doHttpCall(urlAsString, "PUT", doc);
}
/**
* Post should be used when a resource should be posted to a server.
*
* #param urlAsString
* the url to the resource.
* #param doc
* the content to put
* #return The response from the server.
*/
public String post(String urlAsString, String doc) {
return doHttpCall(urlAsString, "POST", doc);
}
/**
* Mostly to be considered as a get without the contents, Here implemented as an is the resource available function.
*
* #param urlAsString
* the url to the resource.
* #return The responseMessage from the server.
*/
public String head(String urlAsString) {
return doHttpCall(urlAsString, "HEAD", null);
}
/**
* Deletes a resource from an url. Be careful!
*
* #param urlAsString
* The url to the resource to delete.
* #return The response from the server.
*/
public String delete(String urlAsString) {
try {
return doHttpCall(urlAsString, "DELETE", null);
} catch (Exception e) {
Log.warn("No object to delete found at " + urlAsString + ".");
return "No object to delete found at " + urlAsString + ".";
}
}
/**
* This method does the actual HTTP communication to simplify the methods above.
*
* #param urlAsString
* The url to resource in question.
* #param method
* The method to be used.
* #param doc
* The resource to send or null if none.
* #return The response from the server.
*/
private String doHttpCall(String urlAsString, String method, String doc) {
StringBuffer result = new StringBuffer();
HttpURLConnection httpUrlConnection = null;
try {
URL url = new URL(urlAsString);
httpUrlConnection = (HttpURLConnection) url.openConnection();
httpUrlConnection.setDoInput(true);
httpUrlConnection.setRequestMethod(method);
if (url.getUserInfo() != null) {
String basicAuth = "Basic " + new String(new Base64().encode(url.getUserInfo().getBytes()));
httpUrlConnection.setRequestProperty("Authorization", basicAuth);
}
httpUrlConnection.setRequestProperty("Content-Length", "0");
for (String key : requestProperties.keySet()) {
httpUrlConnection.setRequestProperty(key, requestProperties.get(key));
}
if (doc != null && !doc.isEmpty()) {
httpUrlConnection.setDoOutput(true);
httpUrlConnection.setRequestProperty("Content-Length", "" + doc.getBytes(charsetName));
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(httpUrlConnection.getOutputStream(),
charsetName);
outputStreamWriter.write(doc);
outputStreamWriter.close();
}
readInputStream(result, httpUrlConnection.getInputStream());
} catch (RuntimeException e) {
Log.info(e.getMessage());
} catch (MalformedURLException e) {
Log.warn("The url '" + urlAsString + "' is malformed.");
} catch (IOException e) {
try {
result.append(e.getMessage());
readInputStream(result, httpUrlConnection.getErrorStream());
if ("".equals(result.toString())) {
result.append("Error ");
result.append(httpUrlConnection.getResponseCode());
result.append(" : ");
result.append(httpUrlConnection.getResponseMessage());
result.append(". Exception message is: [");
result.append(e.getMessage());
result.append("]");
}
} catch (IOException e1) {
}
} finally {
if ("HEAD".equalsIgnoreCase(method)) {
try {
result.append(httpUrlConnection.getResponseMessage());
} catch (IOException e) {
Log.fatal("This is as low as we can get, nothing worked!");
e.printStackTrace();
}
}
if (httpUrlConnection != null)
httpUrlConnection.disconnect();
}
return result.toString();
}
/**
* Local helper method that reads data from an inputstream.
*
* #param result
* The read text.
* #param inputStream
* The stream to read.
* #throws UnsupportedEncodingException
* #throws IOException
*/
private void readInputStream(StringBuffer result, InputStream inputStream) throws UnsupportedEncodingException,
IOException {
if (inputStream == null)
throw new IOException("No working inputStream.");
InputStreamReader streamReader = new InputStreamReader(inputStream, charsetName);
BufferedReader bufferedReader = new BufferedReader(streamReader);
String row;
while ((row = bufferedReader.readLine()) != null) {
result.append(row);
result.append("\n");
}
bufferedReader.close();
streamReader.close();
}
/**
* A main method to provide the possibility to use this exact class from the command line.
* <p>
* usage:
* <code>java -cp target/classes/. path.to.the.class.Curl http://server.domain.nu:port/path/to/resource method [data]</code>
* </p>
*
* #param args
* in order: url method data
*/
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("usage: Curl path method [data]");
System.exit(0);
}
String url = args[0];
String method = args[1];
String data = args.length == 3 ? args[2] : null;
Curl curl = new Curl();
if (method.equals("head")) {
System.out.println(curl.head(url));
System.exit(0);
}
if (method.equals("put")) {
System.out.println(curl.put(url, data));
System.exit(0);
}
System.out.println(curl.doHttpCall(url, method, data));
}
}
Try change configuration parameter required_valid_user to true
When I am using h:selectOneRadio and supplying the list of values in a list as
the entire radio button section is exposed as a single unbroken list. I need to arrange it in 3 columns. I have tried giving
<h:panelGrid id="radioGrid" columns="3">
<h:selectOneRadio id="radio1" value="#{bean.var}">
<f:selectItems id="rval" value="#{bean.list}"/>
</h:selectOneRadio>
</h:panelGrid>
But there is no difference in the rendered section. Its not broken up into columns. What am I doing wrong?
I've adapted the code given by Damo, to work with h:selectOneRadio instead of h:selectManycheckbox. To get it working you will need to register it in your faces-config.xml, with:
<render-kit>
<renderer>
<component-family>javax.faces.SelectOne</component-family>
<renderer-type>javax.faces.Radio</renderer-type>
<renderer-class>test.components.SelectOneRadiobuttonListRenderer</renderer-class>
</renderer>
</render-kit>
To compile it, you will also need the JSF implementation (typically found in some sort of jsf-impl.jar in you app server).
The code outputs the radiobuttons in divs, instead of a table. You can then use CSS to style them however you would like. I would suggest giving a fixed width to the checkboxDiv and inner divs, and then having the inner divs display as inline blocks:
div.radioButtonDiv{
width: 300px;
}
div.radioButtonDiv div{
display: inline-block;
width: 100px;
}
Which should give the 3 columns you are looking for
The code:
package test.components;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UISelectMany;
import javax.faces.component.UISelectOne;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.model.SelectItem;
import com.sun.faces.renderkit.RenderKitUtils;
import com.sun.faces.renderkit.html_basic.MenuRenderer;
import com.sun.faces.util.MessageUtils;
import com.sun.faces.util.Util;
/**
* This component ensures that h:selectOneRadio doesn't get rendered using
* tables. It is adapted from the code at:
* http://www.blog.locuslive.com/?p=15
*
* To register it for use, place the following in your faces config:
*
* <render-kit>
* <renderer>
* <component-family>javax.faces.SelectOne</component-family>
* <renderer-type>javax.faces.Radio</renderer-type>
* <renderer-class>test.components.SelectOneRadiobuttonListRenderer</renderer-class>
* </renderer>
* </render-kit>
*
* The original comment is below:
*
* ----------------------------------------------------------------------------- *
* This is a custom renderer for the h:selectManycheckbox
* It is intended to bypass the incredibly sucky table based layout used
* by the standard component.
*
* This layout uses an enclosing div with divs for each input.
* This gives a default layout similar to a vertical layout
* The layout can then be controlled by css
*
* This renderer assigns an class of "checkboxDiv" to the enclosing div
* The class and styleClass attributes are then applied to the internal
* divs that house the inputs
*
* The following attributes are ignored as they are no longer required when using CSS:
* - pageDirection
* - border
*
* Note that I am not supporting optionGroups at this stage. They would be relatively
* easy to implement with another enclosing div
*
* #author damianharvey
*
*/
public class SelectOneRadiobuttonListRenderer extends MenuRenderer {
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
if (context == null) {
throw new NullPointerException(
MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
"context"));
}
if (component == null) {
throw new NullPointerException(
MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
"component"));
}
// suppress rendering if "rendered" property on the component is
// false.
if (!component.isRendered()) {
return;
}
ResponseWriter writer = context.getResponseWriter();
assert(writer != null);
writer.startElement("div", component);
if (shouldWriteIdAttribute(component)) {
writeIdAttributeIfNecessary(context, writer, component);
}
writer.writeAttribute("class", "radioButtonDiv", "class");
Iterator items = RenderKitUtils.getSelectItems(context, component).iterator();
SelectItem curItem = null;
int idx = -1;
while (items.hasNext()) {
curItem = (SelectItem) items.next();
idx++;
renderOption(context, component, curItem, idx);
}
writer.endElement("div");
}
protected void renderOption(FacesContext context, UIComponent component, SelectItem curItem, int itemNumber)
throws IOException {
ResponseWriter writer = context.getResponseWriter();
assert(writer != null);
// disable the check box if the attribute is set.
String labelClass = null;
boolean componentDisabled = Util.componentIsDisabled(component);
if (componentDisabled || curItem.isDisabled()) {
labelClass = (String) component.
getAttributes().get("disabledClass");
} else {
labelClass = (String) component.
getAttributes().get("enabledClass");
}
writer.startElement("div", component); //Added by DAMIAN
String styleClass = (String) component.getAttributes().get("styleClass");
String style = (String) component.getAttributes().get("style");
if (styleClass != null) {
writer.writeAttribute("class", styleClass, "class");
}
if (style != null) {
writer.writeAttribute("style", style, "style");
}
writer.startElement("input", component);
writer.writeAttribute("name", component.getClientId(context), "clientId");
String idString = component.getClientId(context) + NamingContainer.SEPARATOR_CHAR + Integer.toString(itemNumber);
writer.writeAttribute("id", idString, "id");
String valueString = getFormattedValue(context, component, curItem.getValue());
writer.writeAttribute("value", valueString, "value");
writer.writeAttribute("type", "radio", null);
Object submittedValues[] = getSubmittedSelectedValues(context, component);
boolean isSelected;
Class type = String.class;
Object valuesArray = null;
Object itemValue = null;
if (submittedValues != null) {
valuesArray = submittedValues;
itemValue = valueString;
} else {
valuesArray = getCurrentSelectedValues(context, component);
itemValue = curItem.getValue();
}
if (valuesArray != null) {
type = valuesArray.getClass().getComponentType();
}
// I don't know what this does, but it doens't compile. Commenting it
// out doesn't seem to hurt
// Map<String, Object> requestMap = context.getExternalContext().getRequestMap();
// requestMap.put(ConverterPropertyEditorBase.TARGET_COMPONENT_ATTRIBUTE_NAME,
// component);
Object newValue = context.getApplication().getExpressionFactory().
coerceToType(itemValue, type);
isSelected = isSelected(newValue, valuesArray);
if (isSelected) {
writer.writeAttribute(getSelectedTextString(), Boolean.TRUE, null);
}
// Don't render the disabled attribute twice if the 'parent'
// component is already marked disabled.
if (!Util.componentIsDisabled(component)) {
if (curItem.isDisabled()) {
writer.writeAttribute("disabled", true, "disabled");
}
}
// Apply HTML 4.x attributes specified on UISelectMany component to all
// items in the list except styleClass and style which are rendered as
// attributes of outer most table.
RenderKitUtils.renderPassThruAttributes(writer, component, new String[] { "border", "style" });
RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer, component);
writer.endElement("input");
writer.startElement("label", component);
writer.writeAttribute("for", idString, "for");
// if enabledClass or disabledClass attributes are specified, apply
// it on the label.
if (labelClass != null) {
writer.writeAttribute("class", labelClass, "labelClass");
}
String itemLabel = curItem.getLabel();
if (itemLabel != null) {
writer.writeText(" ", component, null);
if (!curItem.isEscape()) {
// It seems the ResponseWriter API should
// have a writeText() with a boolean property
// to determine if it content written should
// be escaped or not.
writer.write(itemLabel);
}
else {
writer.writeText(itemLabel, component, "label");
}
}
writer.endElement("label");
writer.endElement("div"); //Added by Damian
}
// ------------------------------------------------- Package Private Methods
String getSelectedTextString() {
return "checked";
}
/** For some odd reason this is a private method in the MenuRenderer superclass
*
* #param context
* #param component
* #return
*/
private Object getCurrentSelectedValues(FacesContext context,
UIComponent component) {
if (component instanceof UISelectMany) {
UISelectMany select = (UISelectMany) component;
Object value = select.getValue();
if (value instanceof Collection) {
Collection<?> list = (Collection) value;
int size = list.size();
if (size > 0) {
// get the type of the first element - Should
// we assume that all elements of the List are
// the same type?
return list.toArray((Object[]) Array.newInstance(list.iterator().next().getClass(), size));
}
else {
return ((Collection) value).toArray();
}
}
else if (value != null && !value.getClass().isArray()) {
logger.warning("The UISelectMany value should be an array or a collection type, the actual type is " + value.getClass().getName());
}
return value;
}
UISelectOne select = (UISelectOne) component;
Object returnObject;
if (null != (returnObject = select.getValue())) {
Object ret = Array.newInstance(returnObject.getClass(), 1);
Array.set(ret, 0, returnObject);
return ret;
}
return null;
}
/** For some odd reason this is a private method in the MenuRenderer superclass
*
* #param context
* #param component
* #return
*/
private Object[] getSubmittedSelectedValues(FacesContext context, UIComponent component) {
if (component instanceof UISelectMany) {
UISelectMany select = (UISelectMany) component;
return (Object[]) select.getSubmittedValue();
}
UISelectOne select = (UISelectOne) component;
Object returnObject;
if (null != (returnObject = select.getSubmittedValue())) {
return new Object[] { returnObject };
}
return null;
}
/** For some odd reason this is a private method in the MenuRenderer superclass
*
* #param itemValue
* #param valueArray
* #return
*/
private boolean isSelected(Object itemValue, Object valueArray) {
if (null != valueArray) {
if (!valueArray.getClass().isArray()) {
logger.warning("valueArray is not an array, the actual type is " + valueArray.getClass());
return valueArray.equals(itemValue);
}
int len = Array.getLength(valueArray);
for (int i = 0; i < len; i++) {
Object value = Array.get(valueArray, i);
if (value == null) {
if (itemValue == null) {
return true;
}
}
else if (value.equals(itemValue)) {
return true;
}
}
}
return false;
}
}
The h:panelGrid contains only one child (a h:selectOneRadio), so it will only ever render one column. The h:selectOneRadio renders a HTML table too. Its renderer only offers two layouts (lineDirection and pageDirection).
You have a few options
use JavaScript to modify the table after page load
find a 3rd party control that implements the functionality you want
write your own selectOneRadio control
Tomahawk does the magic! Check it out!
http://wiki.apache.org/myfaces/Display_Radio_Buttons_In_Columns