I'm trying to dynamically display an image in primefaces using the p:graphicImage tag as follows:
<p:graphicImage value="#{submissionBean.contestImage}">
<f:param name="imageName"
value="#{contestBean.createContest.submissions[0].fileName}" />
</p:graphicImage>`
The managed bean is as follows:
#ManagedProperty("#{param.imageName}")
private String imageName;
public String getImageName()
{
return imageName;
}
public void setImageName(String imageName)
{
this.imageName = imageName;
}
private StreamedContent contestImage;
public StreamedContent getContestImage()
{
FacesContext context = FacesContext.getCurrentInstance();
if (imageName == null)
imageName = Constants.SUBMISSION_FILE_DIR + "/" + "sacxzx_asdsdaas_icon.png";
if (context.getRenderResponse())
{
// So, we're rendering the view. Return a stub StreamedContent so
// that it will generate right URL.
return new DefaultStreamedContent();
}
else
{
return new DefaultStreamedContent(this.getClass().getResourceAsStream(Constants.SUBMISSION_FILE_DIR + "/" + imageName));
}
}
I'm always getting the error of "SEVERE: Error in streaming dynamic resource."
Checking the URL for the image seems just fine:
http://localhost:8080/mashup/javax.faces.resource/dynamiccontent.xhtml?ln=primefaces&pfdrid=pfdrid_4290aa0c-8eef-45ea-a281-638e460e33bf&imageName=sacxzx_asdsdaas_icon.png
Any idea why this is?
Thanks!
Should be SessionScoped. As method getContestImage() is called multiple times during page processing, it is better to create the stream only once.
Related
I'm trying to display a PDF through Primefaces. The PDF belongs to some item that the user picks from a list, and is supposed to be displayed on the detail page corresponding to that item. The item gets passed from the list bean to the detail bean via flash memory.
I'm providing the PDF as a StreamedContent through a #ViewScoped bean:
#Named
#ViewScoped
public class ItemDetailPageBean implements Serializable {
private StreamedContent pdf;
#PostConstruct
public void init() {
detailItem = (Item) FacesContext.getCurrentInstance().getExternalContext().getFlash().get("detailItem");
if (detailAntrag != null) {
pdf = loadPdf(detailItem.getPdf());
}
}
public StreamedContent loadPdf(byte[] byteArray) {
if (byteArray != null) {
ByteArrayInputStream stream = new ByteArrayInputStream(byteArray);
stream.mark(0);
return new DefaultStreamedContent(stream, "application/pdf");
} else {
return null;
}
}
public StreamedContent getPdf() {
if (FacesContext.getCurrentInstance().getRenderResponse()) {
return new DefaultStreamedContent();
} else {
if (pdf != null)
{
try {
pdf.getStream().reset();
} catch (IOException e) {
logger.debug("Error while resetting PDF stream: ", e);
}
}
return pdf;
}
}
public void setPdf(StreamedContent pdf) {
this.pdf = pdf;
}
}
Marking and resetting the stream happens since I've read that it's a problem if the stream gets read from multiple times.
However, when I'm trying to display the PDF through itemDetailPageBean.pdf I'm getting an error:
Error in streaming dynamic resource.:
org.jboss.weld.contexts.ContextNotActiveException:
WELD-001303: No active contexts for scope type javax.faces.view.ViewScoped
I tried to provide the PDF through a #ApplicationScoped bean, but the problem stands that this bean needs to know which PDF to use. And when I'm trying to pass the PDF, or even the ID of the corresponding item, from my ViewScoped bean, I'm getting that same error.
I've read about how there are different requests for the page itself and for the PDF, and how there are two different ViewScoped beans - but my understanding ends there. Would be very grateful if you could clear things up a bit! I'm still a bit confused by faces request lifecycles tbh.
Thanks in advance!
I'm using PrimeFaces 6.2
Hi everyone. As mentionned in the title, I need to open a new tab when a user clicks on a link (which is dynamically generated). I tried 2 solutions for now, and none of them works entirely :
1st solution : attributes url and target in PrimeFaces component
Facelet :
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" url="#{arboParObjView.sessionService.lienUrl()}" target="_blank"/>
</p:contextMenu>
View :
#Named(value="arboParObjView")
#ViewScoped
public class ArboParObjView implements Serializable
{
#Inject
SessionService sessionService;
private TreeNode selectedNode //changes everytime a node is selected - both right and left clicks work
...some code here...
public void genererLienBirt() //called everytime the selectedNode value is changed
{
String libelle="";
if (selectedNode != null)
{
//code to find the id of the associated to the selected node.
//I need the id because I want to pass it as a parameter of the link
//And this part of code works well
sessionService.setIdMesure(idMesure);
}
}
}
Session Service :
#Named(value="sessionService")
#SessionScoped
public class SessionService implements Serializable
{
private LienURL lienUrl = new LienURL();
public String lienUrl()
{
String lien = "";
if (idMesure != null)
{
lien = lienUrl.getUrl();
lien += idMesure.toString();
return lien;
}
return "";
}
}
Bean :
public class LienURL
{
private String url;
public LienURL()
{
this.url = "myLink&BirtParameter="; //The base link with a Birt parameter waiting for the idMesure to be passed.
}
}
This solution doesn't work. When the user click on the menu item of the context menu component, it's opening a new tab but the opened page is the same as the one the user just leaved. I think that's because the PF's attribute url loads the url once (and the first time, my url is null because the idMesure isn't filled yet), and it just ignores the good link I try to pass after idMesure is filled.
2nd solution : use the redirect of the FacesContext
Facelet :
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" actionListener="#{arboParObjView.sessionService.lienUrl()}" />
</p:contextMenu>
Service :
#Named(value="sessionService")
#SessionScoped
public class SessionService implements Serializable
{
private LienURL lienUrl = new LienURL();
public void lienUrl() throws IOException
{
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String url = lienUrl.getUrl()+idMesure.toString();
ec.redirect(url);
}
}
The bean and the view don't change. It's the same as in the 1st solution.
The second solution works better than the first one. It is opening the good page with the good url, but in the same tab as the page where the user was. Is there a way to use the FacesContext redirect, but in another tab, as the target="_blank" do (the target only works with the url attribute) ? Or is there a way to make the url attribute read other urls than the first passed (which is null) ?
Thanks, and excuse my english.
Please use target="_blank" in p:menuitem only in second solution and it should work.
Below is updated code
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" actionListener="#{arboParObjView.sessionService.lienUrl()}" target="_blank" />
</p:contextMenu>
and
public void lienUrl() throws IOException
{
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String url = lienUrl.getUrl()+idMesure.toString();
ec.redirect(url);
}
Thanks to all the contributors for their help. Solution below :
View :
#Named(value="arboParObjView")
#ViewScoped
public class ArboParObjView implements Serializable
{
#Inject
private TreePodeService treePodeService;
private TreeNode selectedNode;
private Integer idMesure;
private String lienOplBirt;
...
//redirect to the generated link (called by the UI)
public void redirectOpl()
{
try {
FacesContext.getCurrentInstance().getExternalContext.redirect(lienOplBirt);
} catch (IOException e) {
e.printStackTrace();
}
}
//generate the Birt Link
public void genererLienBirt()
{
String libelle = "";
if (selectedNode != null)
{
libelle = selectedNode.getData().toString();
VArboParObjectifsParents mesureSelected = treePodeService.getPodeArboObjParentDao().findByLibelle(libelle);
idMesure = mesureSelected.getIdRoot();
}
lienOplBirt = "https://theLinkToPass"+"&RP_idMesure="+this.idMesure;
}
...
//Execute the genererLienBirt() method everytime selectedNode's value changes
public void setSelectedTreeNode(TreeNode selectedNode) {
if (selectedNode != this.selectedNode)
{
this.selectedNode = selectedNode;
genererLienBirt();
}
this.selectedNode = selectedNode;
}
}
Facelet (UI)
<p:menuitem value="OPL" includeViewParams="true" action="#{arboParObjView.redirectOpl()}" ajax="false" />
I created a fileupload dialog and a image gallery on a jsf page. After each image upload the gallery should show all so far uploaded images. The images will be stored in a backend bean and should be fetched by the gallery dynamically from the backend bean. For some reason the gallery shows the image labels uploaded but not the referring image since the image resource could not be found.
I use spring, primefaces on tomcat. Thanks for any help in advance!
My JSF Page:
<p:fileUpload id="imageUpldoad" update="galleryPanel" fileUploadListener="#{wizzardBean.handleFileUpload}" mode="advanced" dragDropSupport="true"
sizeLimit="10000000" multiple="true" auto="false" fileLimit="100" allowTypes="/(\.|\/)(gif|jpe?g|png)$/" />
<p:panel id="galleryPanel">
<p:galleria id="gallery" value="#{wizzardBean.getHotelImages()}" var="img" panelWidth="500" panelHeight="313" showCaption="true" rendered="#{wizzardBean.showGallery()}">
<p:graphicImage name="#{img.name}" value="#{wizzardBean.hotelImage}" alt="Image Description for #{img.name}" title="#{img}">
<f:param id="imgId" name="imgId" value="#{img.id}" />
</p:graphicImage>
</p:galleria>
My Backend Bean:
public class WizzardBean extends BaseBean {
private List<HotelImage> hotelImages;
public void handleFileUpload(FileUploadEvent event) throws IOException {
if (event.getFile() != null) {
HotelImage hotelImage = new HotelImage(hotelImages.size(), event.getFile().getFileName(), event.getFile());
hotelImages.add(hotelImage);
}
}
public StreamedContent getHotelImage() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
String photoId = externalContext.getRequestParameterMap().get("imgId");
if (photoId == null || photoId.equals("")) {
return null;
} else {
int parsedId = Integer.parseInt(photoId);
return hotelImages.get(parsedId).getImage();
}
}
}
The HotelImage class:
public class HotelImage {
private int id;
private String name;
private StreamedContent image;
public HotelImage(int id, String name, UploadedFile file) {
this.id = id;
this.name = name;
try {
image = new DefaultStreamedContent(file.getInputstream(), "image/jpg");
} catch (IOException e) {
}
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public StreamedContent getImage() {
return image;
}
The browser says:
<img id="mainFormId:j_idt52:j_idt55" src="RES_NOT_FOUND" alt="Image Description for twitter.png" title="twitter.png">
There are several problems with this approach. Those boil down to the incorrect assumption that an uploaded file, an InputStream and StreamedContent can be read and reused multiple times. This is incorrect. The uploaded file will be sent only once and only be available during the original request, and the streams can be read only once after they are created.
Moreover, having a PrimeFaces-specific StreamedContent or even UploadedFile as a bean property is wrong. The bean property should at least be a File referring the physical file on server's local disk file system, or a byte[] representing the raw content in server memory or a Long representing the insert ID of blob entry in database.
You need to adjust your code to save the uploaded file content to a permanent storage location as soon as possible it comes in and then assign the result as a bean property of type File or byte[] or Long. Then, let the rest of the code use that bean property instead to create a StreamedContent within the getter method. Do absolutely not assign that StreamedContent to another bean property.
You can find concrete and elaborate examples in the answer of the questions linked below.
How to save uploaded file in JSF
Display dynamic image from database or remote source with p:graphicImage and StreamedContent
I have an application, which uses PrimeFaces Mobile to display images.
Sometimes, but not always, the image is not displayed fully - only the top part.
The XHTML code of the page with that image looks like this:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:pm="http://primefaces.org/mobile">
<f:view renderKitId="PRIMEFACES_MOBILE"/>
<h:head>
</h:head>
<f:event listener="#{main.loadFirstImage}" type="preRenderView" />
<h:body id="body">
<pm:page id="page">
<pm:header title="myapp">
</pm:header>
<pm:content id="content">
<h:form>
<p:graphicImage id="image" rendered="false" value="#{main.currentImage()}"
cache="false">
</p:graphicImage>
[...]
</h:form>
</pm:content>
<pm:footer title="m.myapp.com"></pm:footer>
</pm:page>
</h:body>
</html>
And the main bean has following code:
#ManagedBean(name = "main")
#SessionScoped
public class MainView {
private byte[] currentImageData;
private byte[] productId;
private byte[] imageId;
public void loadFirstImage()
{
// This method initializes currentImageData
fetchNextImage();
}
[...]
public StreamedContent currentImage()
{
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
return new DefaultStreamedContent();
}
else {
return new DefaultStreamedContent(new ByteArrayInputStream(currentImageData));
}
}
[...]
}
How can I fix this error?
Update 1 (03.11.2014 23:21 MSK):
I've tried following to fix the error:
1) Disabling cache for all elements of that Primefaces page.
2) Disabling response chunking by setting maxExtensionSize and maxTrailerSize (server.xml) to -1.
3) Adding a filter with following doFilter:
#Override
public void doFilter(final ServletRequest aServletRequest,
final ServletResponse aServletResponse,
final FilterChain aFilterChain) throws IOException, ServletException {
System.out.println("aServletRequest instanceof HttpServletRequest: " +
(aServletRequest instanceof HttpServletRequest));
if (aServletRequest instanceof HttpServletRequest)
{
final HttpServletRequest request = (HttpServletRequest) aServletRequest;
final String requestURI = request.getRequestURI().toLowerCase();
if (!requestURI.endsWith("/javax.faces.resource/dynamiccontent.properties"))
{
aFilterChain.doFilter(aServletRequest, aServletResponse);
}
}
}
4) Changing the currentImage method to
public StreamedContent currentImage()
{
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
// So, we're rendering the view. Return a stub StreamedContent so that it will generate right URL.
return new DefaultStreamedContent();
}
else {
String mimeType = null;
if (imageFileName.toLowerCase().endsWith(".png"))
{
mimeType = "image/png";
}
else if (imageFileName.toLowerCase().endsWith(".jpeg") || imageFileName.toLowerCase().endsWith(".jpg"))
{
mimeType = "image/jpeg";
}
// So, browser is requesting the image. Return a real StreamedContent with the image bytes.
return new DefaultStreamedContent(new ByteArrayInputStream(currentImageData), mimeType);
}
}
But it still doesn't work. I wrote a piece of code in another web application and using different framework (Vaadin), which displays images from the same source.
I get the same error (images are displayed only partially).
From this I conclude that the error must occur
when images are retrieved from a particular and/or
when images are saved in MongoDB.
Code for retrieving images from URL
If the error occurs during reading the image, it occurs in the following method:
protected Binary readImage(final String viewItemURL) {
InputStream inputStream = null;
Binary image = null;
try
{
inputStream = new URL(viewItemURL).openStream();;
byte bytes[] = new byte[inputStream.available()];
inputStream.read(bytes);
image = new Binary(bytes);
}
catch (final IOException exception)
{
LOGGER.error("", exception);
}
finally
{
IOUtils.closeQuietly(inputStream);
}
return image;
}
viewItemURL is the URL of the image.
Code for saving image in MongoDB
If the problem is with saving images in the database, it occurs in the following method:
protected void saveProductImages(final byte[] aNewProductId, final List<String> aPictureUrls,
final IMongoPersistenceState aPersistenceState) {
final DB db = aPersistenceState.getDb();
final DBCollection productImagesColl = db.getCollection(
MyAppPersistenceAction.COLLECTION_USER_PRODUCT_IMAGES);
for (final String curPictureUrl : aPictureUrls)
{
final Binary imageData = readImage(curPictureUrl);
final Map<String,Object> map = new HashMap<String, Object>();
map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_CREATOR_EMAIL, CREATOR_EMAIL);
map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_PRODUCT_ID, aNewProductId);
map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_DATA, imageData);
final String fileName = extractFileName(curPictureUrl);
map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_FILE_NAME, fileName);
map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_MIME_TYPE, getMimeType(fileName));
map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_IS_DELETED, Boolean.FALSE);
productImagesColl.insert(WriteConcern.SAFE, createRecordObject(map));
}
}
Your readImage() method has a major bug:
byte bytes[] = new byte[inputStream.available()];
The InputStream#available() doesn't do what you think it does. It doesn't return the total content length which is what the remainder of the code is expecting. It returns the amount of bytes available for reading without blocking all other threads (i.e. bytes which are currently already put in hardware buffer). This totally explains why you get only that part of the image to display.
No need to be ashamed. Practically all Java starters make the same mistake. The right way to read an InputStream fully is to invoke any read() method on it as long as until it returns -1 indicating EOF (end of file). You can find a bunch of examples and utility library shortcuts in this related question: Convert InputStream to byte array in Java.
Here's a full rewrite of readImage() method doing the right thing, making use of IOUtils which you appear to already have at your hands (and Java 7's try-with-resources with AutoCloseable):
protected Binary readImage(final String viewItemURL) {
try (InputStream inputStream = new URL(viewItemURL).openStream()) {
return new Binary(IOUtils.toByteArray(inputStream));
}
catch (final IOException exception) {
LOGGER.error("", exception);
return null;
}
}
I'm trying to use http://code.google.com/p/kaptcha/ which looks like a very easy way to include CAPTCHA. My demo app is JSF and although the instructions are simple for JSP, I don't know how to use them in JSF. How do I translate this in JSF?
In your code that manages the submit action:
String kaptchaExpected = (String)request.getSession()
.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
String kaptchaReceived = request.getParameter("kaptcha");
if (kaptchaReceived == null || !kaptchaReceived.equalsIgnoreCase(kaptchaExpected))
{
setError("kaptcha", "Invalid validation code.");
}
I tried putting it in my:
public String button1_action() {
// TODO: Process the action.
return "success";
}
but it doesn't understand the request object :(
This equivalent JSF action should do it:
// bind to <h:inputText value="#{thisbean.kaptchaReceived}" />
private String kaptchaReceived;
public String getKaptchaReceived() {
return kaptchaReceived;
}
public void setKaptchaReceived(String kaptcha) {
kaptchaReceived = kaptcha;
}
public String button1_action() {
if (kaptchaReceived != null) {
FacesContext context = FacesContext
.getCurrentInstance();
ExternalContext ext = context.getExternalContext();
Map<String, Object> session = ext.getSessionMap();
String kaptchaExpected = session
.get(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
if (kaptchaReceived.equalsIgnoreCase(kaptchaExpected)) {
return "success";
}
}
return "problem";
}
This assumes that you want to use h:inputText and h:graphicImage in your JSF view instead of HTML elements.
Implementing validator is another easy way to validate the kaptcha.
<h:inputText id="kaptcha" autocomplete="off" required="true">
<f:validator validatorId="kaptchaValidator" />
</h:inputText>
<h:message for="kaptcha" styleClass="errorMessage"/>
--- Validator ---
public class KaptchaValidator implements Validator {
#Override
public void validate(FacesContext facesContext, UIComponent uiComponent, Object value) throws ValidatorException {
HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(true);
String kaptchaExpected = (String) session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
String kaptchaReceived = (String) value;
if (kaptchaReceived == null || !kaptchaReceived.equalsIgnoreCase(kaptchaExpected)) {
FacesMessage message = new FacesMessage();
message.setDetail("Invalid Security Code.");
message.setSummary("Invalid security code.");
message.setSeverity(FacesMessage.SEVERITY_INFO);
throw new ValidatorException(message);
}
}
You can retrieve the request object from the JSF External Context, which is accessible from the FacesContext, using the following code:
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
Edit (thanks to McDowell) :
Another way is to use the FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap() method to access the request parameters...