JSF – Pro9ramming https://pro9ramming.com Software craftsman's blog Wed, 15 Apr 2020 17:51:11 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.3 JSF 2.2 Image upload and serve over Image Servlet https://pro9ramming.com/jsf-2-2-image-upload-and-serve-over-image-servlet/ Wed, 12 Apr 2017 13:35:39 +0000 http://pro9ramming.com/blog/?p=459 Continue reading JSF 2.2 Image upload and serve over Image Servlet]]> Purpose of this tutorial is to make image upload (with validation) in JSF 2.2 and serve uploaded images over Image Servlet. JSF 2.2 has support for file upload, and you don’t need third party libraries anymore. We will upload images on to absolute path and serve them over stream inside registered image servlet. This way of serving image files is perfect if you are using Eclipse with some servlet container (like Tomcat) on Windows OS, and you want to place your images on absolute path in your file system.

First create new “Dynamic Web Project” in Eclipse. First we are going to add some utility classes. StreamUtil class has one static method WriteToOutputStream() which writes data from input to output stream using a buffer with a certain size:

package com.pro9ramming;

import java.io.*;

public class StreamUtil {
	public static final int BUFFER_SIZE = 10240;

	public static void WriteToOutputStream(InputStream input, OutputStream output) throws IOException {
		byte[] buffer = new byte[BUFFER_SIZE];
		int numOfBytes = 0;
		while ((numOfBytes = input.read(buffer)) > 0)
			output.write(buffer, 0, numOfBytes);
	}
}

While there are bytes that need to be copied, this it takes them into the buffer and outputs them to OutputStream. This code is going to be used twice, later in the tutorial (for saving and serving images).

Next class is FileUtil which has 4 static methods:

  • Boolean uploadedFileIsImage(String imageLocation) checks if uploaded file is image by using MIME types. It creates MimetypesFileTypeMap and adds MIME types which need to be checked to that object (all images: png tif jpg jpeg bmp). All image MIME types start with image (for example: image/jpeg, image/bmp etc.).
  • void deleteFile(String fileLocation) deletes file if it exists.
  • List<String> getImagesFromLocation(String location) gets list of file names from absolute path (location) where images are stored. It doesn’t check if those files are images, so that folder needs to be clean (only images).
  • String getImageName(Part image) gets image name from Part interface. First it gets “content-disposition” header and it splits it to get key/value elements (example of header: form-data;name=”form:file”;filename=”some_image.jpg”). Then it looks for “filename” key and it returns it’s value prepended with UUID. UUID (Universally unique identifier) is a random 128 bit number written in certain format. It is used as a prepend because there could be images with the same name in folder where we keep images in.

package com.pro9ramming;

import java.io.File;
import java.util.*;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.http.Part;

public class FileUtil {
	public static Boolean uploadedFileIsImage(String imageLocation) {
        File f = new File(imageLocation);
        MimetypesFileTypeMap mtftp = new MimetypesFileTypeMap();
        mtftp.addMimeTypes("image png tif jpg jpeg bmp");
        String mimetype = mtftp.getContentType(f);
        String type = mimetype.split("/")[0];
        return type.equals("image");
    }
	
	public static void deleteFile(String fileLocation){
		File f = new File(fileLocation);
		if(f.exists())
			f.delete();
	}
	
	public static List<String> getImagesFromLocation(String location) {
		File f = new File(location);
		ArrayList<String> images = new ArrayList<String>(Arrays.asList(f.list()));
		return images;
	}
	
	public static String getImageName(Part image) {
		String[] arr = image.getHeader("content-disposition").split(";");
		for (String content : arr) {
			if (content.trim().startsWith("filename"))
				return UUID.randomUUID().toString() + "_" + content.substring(content.indexOf('=') + 1).trim()
								.replace("\"", "");
		}
		return null;
	}
}

Then create a managed bean that is going to be used for image uploading (let’s call it MainBean). Register it using @ManagedBean@SessionScoped annotations. MainBean has Part interface property (with getter and setter methods). MainBean has 3 methods (except getters and setters) :

  • void outputError(String messageStr) which gets FacesContext and creates FacesMessage which is outputted to “image-upload-form:image” location in .xhtml file.
  • List<String> getImages() gets images by using getImagesFromLocation() method from FileUtil class.
  • String upload() is an action method which is called by commandButton when user uploads it’s image. It gets image name (FileUtil.getImageName() method), creates streams (input and output) and writes image to absolute path. Then it validates if uploaded file is image (FileUtil.uploadedFileIsImage() method), and deletes the image if it is not, with message using FacesMessage from outputError() method. InputStream is stream from Part interface, and OutputStream is simple FileOutputStream. Input is written to output by using StreamUtil.WriteToOutputStream(input, output) static method.

package com.pro9ramming;

import java.io.*;
import java.util.*;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.Part;

@ManagedBean
@SessionScoped
public class MainBean {
	public static final String PATH = "C:/images/";
	private Part image;

	public Part getImage() {
		return image;
	}

	public void setImage(Part image) {
		this.image = image;
	}

	public String upload() {
		try {

			String imageName = FileUtil.getImageName(image);
			if (imageName == null)
				outputError("Image name not available.");
			String imageLocation = PATH + imageName;
			InputStream input = image.getInputStream();
			FileOutputStream output = new FileOutputStream(imageLocation);
			StreamUtil.WriteToOutputStream(input, output);
			input.close();
			output.close();
			if (!FileUtil.uploadedFileIsImage(imageLocation)) {
				outputError("Uploaded file is not an image.");
				FileUtil.deleteFile(imageLocation);
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			outputError("Error while uploading.");
			return null;
		}
		return "gallery";
	}

	private void outputError(String messageStr) {
		FacesContext context = FacesContext.getCurrentInstance();
		FacesMessage msg = new FacesMessage(messageStr);
		context.addMessage("image-upload-form:image", msg);
	}

	public List<String> getImages() {
		return FileUtil.getImagesFromLocation(PATH);
	}

}

That is background logic for uploading. There are 2 views (.xhtml files), let’s say index.xhtml (used for uploading) and gallery.xhtml (used for showing all uploaded images). Index.xhtml contains form with enctype=”multipart/form-data” attribute. Form contains:

  • h:inputFile with attribute value=”#{mainBean.image}”, which is part of JSF 2.2 standard.
  • h:message for showing FacesMessage messages associated with uploading process.
  • h:commandButton which calls upload() action method, when image file is selected.

<!DOCTYPE html>

<html 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">
<h:head>
<title>Image upload</title>
</h:head>
<h:body>
<h:form id="image-upload-form" enctype="multipart/form-data">
  <h:inputFile id="image" value="#{mainBean.image}"/><br />
  <h:message for="image" style="color:red"/><br />
  <h:commandButton value="Upload" action="#{mainBean.upload}"/>
</h:form>
</h:body>
</html>

Gallery.xhtml shows all images from folder where we keep them. It contains ui:repeat which shows all images by calling getImages() method. It also encapsultes h:graphicImage inside h:outputLink tags, so you can view actual size image, and not limited 300×200 size.

<!DOCTYPE html>

<html 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">
<h:head>
<title>Gallery</title>
</h:head>
<h:body>
<h2><h:outputText value="Gallery"/></h2>
<h:graphicImage value="/image/a1AXpew_460sa.gif" /><br />


<ui:repeat var="str" value="#{mainBean.images}">
<h:outputLink value="/JSFImageUpload/image/#{str}">
<h:graphicImage value="/image/#{str}" alt="Image not found" width="300" height="200"/><br /><br />
</h:outputLink>
</ui:repeat>

</h:body>
</html>

 

The final thing is creation of ImageServlet[4]. It uses doGet() method to retrieve requested image. It checks if image exists and it serves it by using FileInputStream and OutputStream from response object. Again writing from input to output stream is being done by using StreamUtil.WriteToOutputStream() method. Before serving, servlet sets “Content-Length” and “Content-Disposition” HTTP headers.

package com.pro9ramming;

import java.io.*;
import java.net.URLDecoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ImageServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		BufferedInputStream input = null;
		BufferedOutputStream output = null;
		try {
			String requestedImage = request.getPathInfo();

			if (requestedImage == null) {
				response.sendError(HttpServletResponse.SC_NOT_FOUND);
				return;
			}

			File image = new File(MainBean.PATH, URLDecoder.decode(
					requestedImage, "UTF-8"));

			if (!image.exists()) {
				response.sendError(HttpServletResponse.SC_NOT_FOUND);
				return;
			}

			String contentType = getServletContext().getMimeType(
					image.getName());

			if (contentType == null || !contentType.startsWith("image")) {
				response.sendError(HttpServletResponse.SC_NOT_FOUND);
				return;
			}

			response.reset();
			response.setBufferSize(StreamUtil.BUFFER_SIZE);
			response.setContentType(contentType);
			response.setHeader("Content-Length", String.valueOf(image.length()));
			response.setHeader("Content-Disposition", "inline; filename=\""
					+ image.getName() + "\"");

			input = new BufferedInputStream(new FileInputStream(image),
					StreamUtil.BUFFER_SIZE);
			output = new BufferedOutputStream(response.getOutputStream(),
					StreamUtil.BUFFER_SIZE);
			StreamUtil.WriteToOutputStream(input, output);

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (output != null)
				output.close();
			if (input != null)
				input.close();
		}
	}

}

And in your web.xml (WebContent/WEB-INF/web.xml) file, you need to register your ImageServlet:

<servlet>
    <servlet-name>imageServlet</servlet-name>
    <servlet-class>com.pro9ramming.ImageServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>imageServlet</servlet-name>
    <url-pattern>/image/*</url-pattern>
</servlet-mapping>

Now you can test your simple image gallery. For more information about certain parts of this tutorial, you can view references.

References :

Part interface 

File upload tutorial

BalusC Image Servlet

UUID reference

Check file type

Extend MimetypesFileTypeMap

]]>
JSF Client User-Agent data retrieve over RESTful web service https://pro9ramming.com/jsf-client-user-agent-data-retrieve-over-restful-web-service/ Sun, 16 Mar 2014 14:41:49 +0000 http://pro9ramming.com/blog/?p=422 Continue reading JSF Client User-Agent data retrieve over RESTful web service]]> Purpose of this tutorial is to get data about client’s user-agent over RESTful web service [1]. and show provided data in JSF page.

The process consists of 3 parts:

  • Get user-agent string from client’s HTTP request
  • Call web service and parse data about User Agent
  • Show data in JSF page to client

User-Agent string can be retrieved from HttpServletRequest object which can be accesses through FacesContext object:

FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest)context.getExternalContext().getRequest();
String userAgentString = request.getHeader("user-agent");

Data is transfered in JSON format, parsing data is achieved through Jackson library [2]. API location is:

http://www.useragentstring.com/?getJSON=all&uas=USER-AGENT-STRING

Replace USER-AGENT-STRING with string retrieved in step one, for example:

Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14

After calling web service (obviously over GET method), you’ll get JSON data, for example:

http://www.useragentstring.com/?getJSON=all&uas=Opera/9.80%20%28Windows%20NT%206.0%29%20Presto/2.12.388%20Version/12.14

which returns:

{  
   "agent_type":"Browser",
   "agent_name":"Opera",
   "agent_version":"12.14",
   "os_type":"Windows",
   "os_name":"Windows Vista",
   "os_versionName":"",
   "os_versionNumber":"",
   "os_producer":"",
   "os_producerURL":"",
   "linux_distibution":"Null",
   "agent_language":"",
   "agent_languageTag":""
}

Next step is reading those values and parsing them, using ObjectMapper from Jackson library [3]. First, for Jackson deserialization (or unmarshalling), class is needed. The UserAgent class looks like:

package com.pro9ramming.jacksontest;

import com.fasterxml.jackson.annotation.*;
 
public class UserAgent {
	@JsonProperty("agent_type")
	private String agentType;
	
	@JsonProperty("agent_name")
	private String agentName;
	
	@JsonProperty("agent_version")
	private String agentVersion;
	
	@JsonProperty("os_type")
	private String osType;
	
	@JsonProperty("os_name")
	private String osName;
	
	@JsonProperty("os_versionName")
	private String osVersionName;
	
	@JsonProperty("os_versionNumber")
	private String osVersionNumber;
	
	@JsonProperty("os_producer")
	private String osProducer;
	
	@JsonProperty("os_producerURL")
	private String osProducerURL;
	
	@JsonProperty("linux_distibution")
	private String linuxDistribution;
	
	@JsonProperty("agent_language")
	private String agentLanguage;
	
	@JsonProperty("agent_languageTag")
	private String agentLanguageTag;

//getters and setters

	@Override
	public String toString() {
		return "UserAgent [agentType=" + agentType + ", agentName=" + agentName
				+ ", agentVersion=" + agentVersion + ", osType=" + osType
				+ ", osName=" + osName + ", osVersionName=" + osVersionName
				+ ", osVersionNumber=" + osVersionNumber + ", osProducer="
				+ osProducer + ", osProducerURL=" + osProducerURL
				+ ", linuxDistribution=" + linuxDistribution
				+ ", agentLanguage=" + agentLanguage + ", agentLanguageTag="
				+ agentLanguageTag + "]";
	}
	
	
}

Annotation @JsonProperty is used to specify properties used in serialization and make sure right JSON identifiers are used. Class property can have different name than JSON property. UserAgent class also has toString() method for showing data to client. After making the UserAgent class, retrieving and parsing data over ObjectMapper is simple:

ObjectMapper mapper = new ObjectMapper();
UserAgent userAgent = mapper.readValue(INPUT-STREAM , UserAgent.class);

You need to replace INPUT-STREAM with input stream from URL class. URL class is instantiated by forming URL encoded string through GET method:

String urlString = "http://www.useragentstring.com/?getJSON=all&uas="+ URLEncoder.encode(USER-AGENT-STRING, "UTF-8");
URL url = new URL(urlString);
//input stream in url.openStream()

Putting everything together:

public String getData(){
		try {
			ObjectMapper mapper = new ObjectMapper();
			String urlString  = "http://www.useragentstring.com/?getJSON=all&uas="+ URLEncoder.encode(getUserAgentString(), "UTF-8");
			UserAgent userAgent = mapper.readValue(new URL(urlString).openStream(),UserAgent.class);
			return userAgent.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "ERROR";
	}
	
	private String getUserAgentString(){
		FacesContext context = FacesContext.getCurrentInstance();
		HttpServletRequest request = (HttpServletRequest)context.getExternalContext().getRequest();
		return request.getHeader("user-agent");
	}

Let’s say that managed bean is called UserAgentBean, so in the .xhtml file, you can specifty next line:

<h:outputText value="#{userAgentBean.data}"/>

That is going to call getData() method, which returns data from UserAgent toString() method. You can format your output as you want.

References:

[1] User-Agent API

[2] JSON processing library – Jackson

[3] Mkyong JSON parsing tutorial

]]>