-
1. Re: Restrict
gavin.king Feb 14, 2007 4:42 PM (in response to tony.herstell1)An empty should do the trick. You presumably have no rule which explicitly assigns permission to read the page.
-
2. Re: Restrict
tony.herstell1 Feb 14, 2007 4:57 PM (in response to tony.herstell1)Cool.
You presumably have no rule which explicitly assigns permission to read the page.
No, Haven't got that far yet...
Still working on the hack to the hack to the hack to get an image to uplaod using IceFaces ;).. which it now does! -
3. Re: Restrict
saeediqbal1 Feb 14, 2007 5:11 PM (in response to tony.herstell1)Tony: Wanna share with us how you do it?
-
4. Re: Restrict
tony.herstell1 Feb 14, 2007 5:33 PM (in response to tony.herstell1)Ugh.. its a bit crappy at the moment...
but here it is:
1. Don't use the progress viewer as that just goes SPLAT (I don't think IceFaces passes to the callback it makes the actual conversationId so you get a "not part of long running conversation" exception.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:s="http://jboss.com/products/seam/taglib" xmlns:ice="http://www.icesoft.com/icefaces/component"> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <link rel="stylesheet" type="text/css" href="./css/risingstars.css" /> <link rel="stylesheet" type="text/css" href="./xmlhttp/css/xp/xp.css" /> <title>Upload</title> </head> <body> <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:s="http://jboss.com/products/seam/taglib" xmlns:ice="http://www.icesoft.com/icefaces/component" template="template.xhtml"> <!-- content --> <ui:define name="title"> <ice:outputText value="#{messages.form_upload}" /> </ui:define> <ui:define name="content"> <ice:form> <ice:panelGrid columns="1"> <fieldset> <legend> <ice:outputText value="#{messages.fieldset_upload}" /> </legend> <!-- Messages --> <ice:panelGrid columns="1"> <ice:messages infoClass="error" globalOnly="true" /> </ice:panelGrid> <ice:panelGrid columns="1"> <fieldset> <legend> <ice:outputText value="#{messages.fieldset_upload}"/> </legend> <ice:panelGrid columns="1"> <ice:inputFile style="border:none; width:400px; height:70px;" actionListener="#{uploadController.action}"/> <!-- progressListener="#{uploadController.progress}" --> <ice:outputProgress id="progress" value="#{uploadController.percent}"/> </ice:panelGrid> <ice:panelGrid columns="1" width="100%"> <div align="right"> <ice:commandButton action="#{uploadController.cancel}" value="#{messages.button_cancel}" immediate="true" type="submit" /> <ice:commandButton id="done" type="submit" value="#{messages.button_done}" immediate="true" action="#{uploadController.done}" /> </div> </ice:panelGrid> </fieldset> </ice:panelGrid> </fieldset> </ice:panelGrid> </ice:form> </ui:define> <!-- content --> </ui:composition> </body> </html>
For the backing bean I stole the code from the seamspace example (Who Hoo!)... (well I am not THAT stupid!)
So... add this to your web.xml:<!-- Content Servlet --> <servlet> <servlet-name>Content Servlet</servlet-name> <servlet-class>nz.co.risingstars.content.ContentServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Content Servlet</servlet-name> <url-pattern>/content/*</url-pattern> </servlet-mapping> <filter> <filter-name>Seam Servlet Filter</filter-name> <filter-class>org.jboss.seam.servlet.SeamServletFilter</filter-class> </filter> <filter-mapping> <filter-name>Seam Servlet Filter</filter-name> <url-pattern>/content/*</url-pattern> </filter-mapping> <!-- file upload Servlet --> <servlet> <servlet-name>uploadServlet</servlet-name> <servlet-class>com.icesoft.faces.component.inputfile.FileUploadServlet</servlet-class> <load-on-startup> 1 </load-on-startup> </servlet> <servlet-mapping> <servlet-name>uploadServlet</servlet-name> <url-pattern>/uploadHtml</url-pattern> </servlet-mapping>
Now to handle the stuff coming in... here is the code...
with the following hack(s):
action is called when the image is uploaded... since you can't pass back a new state to "go to" you are stuffed for now as moving away from the page screws things up..
I tried adding a DONE button to the gui that called my "done" routine but thats crashes... so I hack the action success route to call Done Instead (OUCH!)...
This leaves a transaction incomplete however... HACK!
I then have to quite the browser and run up a new browser (usually) to see the results when I look at my list of xxx's (users or orgs).
Hackity Hackity Hack.
Not long till you get all things going wrong as something barfs over the "hanging" transaction or Hibernate reports a Heap Space error..package nz.co.risingstars.actions.upload; import javax.faces.event.ActionEvent; import java.util.EventObject; /** * @author Tony Herstell * @version $Revision: 1.3 $ $Date: 2007-02-12 22:28:39 $ */ public interface UploadController { public String startUpload(Object selectedObject); public void setPercent(int percent); public int getPercent(); public void action(ActionEvent event); public void progress(EventObject event); public String done(); public String cancel(); public void destroy(); } package nz.co.risingstars.actions.upload; import static javax.persistence.PersistenceContextType.EXTENDED; import com.icesoft.faces.component.inputfile.InputFile; import com.icesoft.faces.webapp.xmlhttp.PersistentFacesState; import com.icesoft.faces.webapp.xmlhttp.RenderingException; import javax.ejb.Remove; import javax.ejb.Stateful; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.faces.event.ActionEvent; import javax.imageio.ImageIO; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.swing.ImageIcon; import nz.co.risingstars.actions.organisation.FindOrganisationController; import nz.co.risingstars.actions.user.FindUserController; import nz.co.risingstars.entities.Image; import nz.co.risingstars.entities.Organisation; import nz.co.risingstars.entities.User; import org.jboss.seam.Component; import org.jboss.seam.annotations.Begin; import org.jboss.seam.annotations.Conversational; import org.jboss.seam.annotations.Destroy; import org.jboss.seam.annotations.End; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Logger; import org.jboss.seam.annotations.Name; import org.jboss.seam.core.Conversation; import org.jboss.seam.core.FacesMessages; import org.jboss.seam.log.Log; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.EventObject; /** * @author Tony Herstell * @version $Revision: 1.4 $ $Date: 2007-02-13 04:46:40 $ */ @Stateful @Name("uploadController") @Conversational public class UploadControllerImpl implements UploadController { /** * The maximum width allowed for image rescaling */ private static final int MAX_IMAGE_WIDTH = 1024; @PersistenceContext(type = EXTENDED) private EntityManager em; @Logger private Log log; @In(required = false) private Conversation conversation; @In(create = true) private transient FacesMessages facesMessages; private enum ParentObjectKind {ORGANISATION, USER}; private ParentObjectKind parentObjectKind = null; private long primaryKey; private int percent = -1; private PersistentFacesState state = null; private Image image = null; @Begin @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public String startUpload(Object selectedObject) { log.info("> startUpload"); String theValueToBeReturned = null; state = PersistentFacesState.getInstance(); if (selectedObject instanceof Organisation) { parentObjectKind = ParentObjectKind.ORGANISATION; primaryKey = ((Organisation)selectedObject).getId(); log.info("Organisation Id was =>" + primaryKey); theValueToBeReturned = "upload"; } else if (selectedObject instanceof User) { parentObjectKind = ParentObjectKind.USER; primaryKey = ((User)selectedObject).getId(); log.info("User Id was =>" + primaryKey); theValueToBeReturned = "upload"; } else { log.error("upload called with object type not supported."); } logConversation("Upload"); log.info("< startUpload"); return theValueToBeReturned; } @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public void setPercent(int percent) { log.info("> setPercent"); this.percent = percent; log.info("< setPercent"); } @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public int getPercent() { log.info("> getPercent"); log.info("< getPercent"); return percent; } //@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) @TransactionAttribute(TransactionAttributeType.REQUIRED) public void action(ActionEvent event) { log.info("> action"); InputFile inputFile = (InputFile) event.getSource(); if (inputFile.getStatus() == InputFile.SAVED) { String fileName = inputFile.getFileInfo().getFileName(); String contentType = inputFile.getFileInfo().getContentType(); File file = inputFile.getFile(); byte[] inputFileAsBytes = getFileAsBytes(file); // Check if the image needs to be rescaled int width = Math.min(MAX_IMAGE_WIDTH, 70); ImageIcon icon = new ImageIcon(inputFileAsBytes); boolean rescale = false; if (width > 0 && width != icon.getIconWidth()) { rescale = true; } byte[] inputFileAsThumbnailAsBytes = null; // Rescale the image if required if (rescale) { inputFileAsThumbnailAsBytes = getRescaledImageAsBytes(contentType, width, icon); } else { inputFileAsThumbnailAsBytes = inputFileAsBytes; } Image image = (Image)Component.getInstance("image", true); image.setName(fileName); image.setType(contentType); image.setThumbnail(inputFileAsThumbnailAsBytes); image.setImage(inputFileAsBytes); image.setVersion(0); this.image = image; } else if (inputFile.getStatus() == InputFile.INVALID) { inputFile.getFileInfo().getException().printStackTrace(); } else if (inputFile.getStatus() == InputFile.SIZE_LIMIT_EXCEEDED) { inputFile.getFileInfo().getException().printStackTrace(); } else if (inputFile.getStatus() == InputFile.UNKNOWN_SIZE) { inputFile.getFileInfo().getException().printStackTrace(); } done(); // HACK! log.info("< action"); } @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) private byte[] getFileAsBytes(File file) { byte[] valueToReturn = null; log.info("> getAsBytes"); InputStream in = null; OutputStream out = null; try { in = new BufferedInputStream(new FileInputStream(file)); ByteArrayOutputStream baos = new ByteArrayOutputStream(); out = new BufferedOutputStream(baos); final int toRead = 1024; byte[] buffy = new byte[toRead]; int read; while ((read = in.read(buffy)) != -1) { out.write(buffy, 0, read); out.flush(); } valueToReturn = baos.toByteArray(); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } finally { // Try to release any resources. try { if (in != null) { in.close(); } } catch (IOException ignored) {} try { if (out != null) { out.close(); } } catch (IOException ignored) {} } log.info("< getAsBytes"); return valueToReturn; } @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) private byte[] getRescaledImageAsBytes(String contentType , int width, ImageIcon icon) { double ratio = (double) width / icon.getIconWidth(); int height = (int) (icon.getIconHeight() * ratio); int imageType = "image/png".equals(contentType) ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB; BufferedImage bImg = new BufferedImage(width, height, imageType); Graphics2D g2d = bImg.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g2d.drawImage(icon.getImage(), 0, 0, width, height, null); g2d.dispose(); String formatName = ""; if ("image/png".equalsIgnoreCase(contentType)) formatName = "png"; else if ("image/jpeg".equalsIgnoreCase(contentType)) formatName = "jpeg"; else if ("image/jpg".equalsIgnoreCase(contentType)) formatName = "jpg"; else if ("image/gif".equalsIgnoreCase(contentType)) formatName = "gif"; ByteArrayOutputStream baos = null; OutputStream out = null; try { baos = new ByteArrayOutputStream(); out = new BufferedOutputStream(baos); try { ImageIO.write(bImg, formatName, out); } catch (IOException e) { e.printStackTrace(); } } finally { // Try to release any resources. try { if (baos != null) { baos.close(); } } catch (IOException ignored) {} try { if (out != null) { out.close(); } } catch (IOException ignored) {} } return baos.toByteArray(); } @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public void progress(EventObject event) { log.info("> progress"); InputFile file = (InputFile) event.getSource(); this.percent = file.getFileInfo().getPercent(); try { if (state != null) { state.render(); } else { System.out.println("state is null"); } } catch (RenderingException ee) { System.out.println(ee.getMessage()); } log.info("< progress"); } //@End //@TransactionAttribute(TransactionAttributeType.REQUIRED) public String done() { log.info("> done"); String valueToReturn = null; em.persist(image); if (parentObjectKind == ParentObjectKind.ORGANISATION) { Organisation organisationToBeUploadedTo = em.find(Organisation.class, primaryKey); if (organisationToBeUploadedTo == null) { log.warn("Organisation to be Uploaded to is not found." + primaryKey); } else { organisationToBeUploadedTo.setPicture(image); em.persist(organisationToBeUploadedTo); em.flush(); FindOrganisationController findOrganisationController = (FindOrganisationController)Component.getInstance("findOrganisationController", false); if (findOrganisationController != null) { // We have to inform the Stateful findOrganisationController that one of its children has been updated. findOrganisationController.updateOrganisationInExistingList(organisationToBeUploadedTo); } facesMessages.addFromResourceBundle("organisation_picture_added"); valueToReturn = "findOrganisation"; } } else if (parentObjectKind == ParentObjectKind.USER) { User userToBeUploadedTo = em.find(User.class, primaryKey); if (userToBeUploadedTo == null) { log.warn("User to be Uploaded to is not found." + primaryKey); } else { userToBeUploadedTo.setPicture(image); em.persist(userToBeUploadedTo); em.flush(); FindUserController findUserController = (FindUserController)Component.getInstance("findUserController", false); if (findUserController != null) { // We have to inform the Stateful findUserController that one of its children has been updated. findUserController.updateUserInExistingList(userToBeUploadedTo); } facesMessages.addFromResourceBundle("user_picture_added"); valueToReturn = "findUser"; } } else { log.error("done called with object type not supported."); } log.info("< done"); return valueToReturn; } @End @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public String cancel() { log.info("> cancel"); String valueToReturn = null; if (parentObjectKind == ParentObjectKind.ORGANISATION) { valueToReturn = "findOrganisation"; } else if (parentObjectKind == ParentObjectKind.USER) { valueToReturn = "findUser"; } else { log.error("cancel called with object type not supported."); } log.info("< cancel"); return valueToReturn; } @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) private void logConversation(String name) { log.info("Starting " + name + " conversation."); if (conversation != null) { log.info("Conversation. (" + conversation.getId() + ")"); } } @Remove @Destroy @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public void destroy() { log.info("> destroy"); log.info("< destroy"); } }
It's going to be sorted out in the next IceFaces release of course ;).. It would be nice to hand over the project with all my hack removed to IceFaces and the next release be dependent on all this code working; but thats pretty unlikely... so I asked for an Alpha of their next release asap!
You will notice I do some image manipulation to store a thunbmnail with the main image.. which SEEMS to work (visually), but until I get this stable.. then I don't really know.
I aim to post this complete project to the wiki when its actually working well enough (post some nasty IceFaces bugs) -
5. Re: Restrict
tony.herstell1 Feb 14, 2007 5:35 PM (in response to tony.herstell1)package nz.co.risingstars.content; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import nz.co.risingstars.entities.Image; import org.jboss.seam.Component; /** * Serves images and other member content * * @author Shane Bryzak */ public class ContentServlet extends HttpServlet { private static final String IMAGES_PATH = "/images"; private byte[] noImage; public ContentServlet() { InputStream noImageFromStorage = getClass().getResourceAsStream("/images/no_image.png"); if (noImageFromStorage != null) { ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buffer = new byte[512]; try { int read = noImageFromStorage.read(buffer); while (read != -1) { out.write(buffer, 0, read); read = noImageFromStorage.read(buffer); } noImage = out.toByteArray(); } catch (IOException e) { } } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (IMAGES_PATH.equals(request.getPathInfo())) { ContentController contentController = (ContentController) Component .getInstance(ContentControllerImpl.class); String id = request.getParameter("id"); Image imageToFetch = null; if (id != null && !"".equals(id)) { imageToFetch = contentController.getImage(Long.parseLong(id)); } String contentType = null; byte[] data = null; if (imageToFetch != null && imageToFetch.getThumbnail() != null && imageToFetch.getThumbnail().length > 0) { contentType = imageToFetch.getType(); data = imageToFetch.getThumbnail(); } else if (noImage != null) { contentType = "image/png"; data = noImage; } if (data != null) { response.setContentType(contentType); } response.getOutputStream().write(data); response.getOutputStream().flush(); } } } package nz.co.risingstars.content; import nz.co.risingstars.entities.Image; public interface ContentController { Image getImage(long imageId); } package nz.co.risingstars.content; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import nz.co.risingstars.entities.Image; import org.jboss.seam.annotations.Name; @Stateless @Name("contentController") public class ContentControllerImpl implements ContentController { @PersistenceContext private EntityManager em; public Image getImage(long imageId) { Image img = em.find(Image.class, imageId); // if (img == null || !Identity.instance().hasPermission("memberImage", "view", img)) // return null; // else return img; } }
-
6. Re: Restrict
tony.herstell1 Feb 14, 2007 5:43 PM (in response to tony.herstell1)I am probably doing horrible horrible things to the database...
But it seems as good as place as any to stuff the images ;)
and makes it more useful as a demo that can be run by the community.
As this was started before SeamGen was as good as it is now then this demo was pretty much hand crafted.
Look to SeamGen for how to write a Seam APP! -
7. Re: Restrict
tony.herstell1 Feb 14, 2007 5:51 PM (in response to tony.herstell1)I am now trying to figure out how to get the image that was uploaded to be shown on the "upload" page (as the transaction has not committed yet).
Basically:
It's been stuffed in the database, but in a transaction that is not committed yet (hack or not!).
It's available in my Upload component as a byte stream etc. or in fact as anything I want!
However, how do I get it onto the final html page?
If I use the servler route then will calling the servlet actually find it on the database?
??? -
8. Re: Restrict
supernovasoftware.com Feb 14, 2007 6:16 PM (in response to tony.herstell1)I put all of my page fragments in WEB-INF and only have pages that I make available in the root directory. This solves this problem for me without have to put any restrictions.
-
9. Re: Restrict
gavin.king Feb 14, 2007 6:18 PM (in response to tony.herstell1)yes, that is a much better way
-
10. Re: Restrict
saeediqbal1 Feb 14, 2007 6:41 PM (in response to tony.herstell1)Thanks Tony. I'll try Jason's advise too
-
11. Re: Restrict
supernovasoftware.com Feb 14, 2007 6:48 PM (in response to tony.herstell1)I also use the following in components.xml
<factory name="basePath" value="#{facesContext.externalContext.request.scheme}://#{facesContext.externalContext.request.serverName}:#{facesContext.externalContext.request.serverPort}#{facesContext.externalContext.request.contextPath}/"/>
Note this is all one line the forum wrapped
and<base href="#{basePath}" />
So i can have the view files in different directories, but still resolve the images and css to the root of the webapp with out having to do ../../images/myimage.jpg
I used to use something like this@Create public void setup() { request = (HttpServletRequest) facesContext.getExternalContext().getRequest(); } @Factory("basePath") public void loadBasePath() { basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/"; }
-
12. Re: Restrict
tony.herstell1 Feb 14, 2007 7:15 PM (in response to tony.herstell1)Very Good tip.
I had real issues with facelet templates and having them reference files in different directories (would have had to have copies of template files or pull templates into templates depending on something else) so I must admit I dumped them all in one directory.
I will move my "sucked in" files under WEB-INF. -
13. Re: Restrict
rmemoria Feb 14, 2007 10:17 PM (in response to tony.herstell1)You may also declare a security-constraint in web.xml like that:
<security-constraint> <display-name>Restrict XHTML Documents</display-name> <web-resource-collection> <web-resource-name>XHTML</web-resource-name> <url-pattern>*.xhtml</url-pattern> <url-pattern>/mail/*</url-pattern> <url-pattern>/templates/*</url-pattern> </web-resource-collection> <auth-constraint> <description>Only Let 'developer's access XHTML pages</description> <role-name>developer</role-name> </auth-constraint> </security-constraint>
I got it from facelets developers guide. I like it 'cause I can restrict complex structures and not only some files.
Ricardo Memória