Sunday, January 04, 2015

Building RESTful Web Services or RESTful Web Services with Java


The below article will show examples of building Simple RESTful Web Services, Restful web service for file upload, Restful web service for file download

  • Building RESTful Web Services with JAX-RS using Jboss rest easy API.
  • RESTful Web Services with Java
  • RESTful Java client with Jboss RESTEasy client framework
  • RESTEasy client for consuming REST APIs
  • Pure java client to invoke Restful web service
  • Using Firefox rest client for testing restful web services.
  • RestEasy client for file upload
  • RestEasy client for file download


I used Jboss rest easy API, You can also use Jersey api to build restful web services.
Lets say we need a restful web service which does the following

  • Employee service - To Create,Get,Update and Delete an employee object.
  • File Service - To upload and download a file.
  • Resteasy client  to download a file (ZIP, PDF...etc)
  • Resteasy client  to upload files (ZIP, PDF...etc)

Java code for building Employee Service

Lets build a java object for Employee object which is JAXB enabled. Let use JAXB annotations.

package com.rama.restful;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/*****
 *   XmlAccessType.NONE --> Tell JAXB not use field name as it's in the class. 
 *                         Instead we will give the names that as to be used  when generating XML. 
 *    
 *     Ideally keep this in common package so that you can share between java client and server                    
 ***/
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "employee")
public class Employee {


    @XmlAttribute(name = "id")
    private int Id;
 
    @XmlAttribute(name="uri")
    private String uri;
 
    @XmlElement(name = "firstName")
    private String firstName;
 
    @XmlElement(name = "lastName")
    private String lastName;
    
 public int getId() {
  return Id;
 }
 public void setId(int id) {
  Id = id;
 }
 public String getFirstName() {
  return firstName;
 }
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 public String getLastName() {
  return lastName;
 }
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
 public String getUri() {
  return uri;
 }
 public void setUri(String uri) {
  this.uri = uri;
 }
 
 
 
}
A list of employee's is wrapped in a Employees Object as shown below.

package com.rama.restful;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "employees")
public class Employees {

 //Will embed every object in the list with <employee> tag. 
 @XmlElement(name="employee") 
 private List<Employee> employees = new ArrayList<Employee>();

 public List<Employee> getEmployees() {
  return employees;
 }

 public void setEmployees(List<Employee> employees) {
  this.employees = employees;
 }
}

Now lets create EmployeeService which is the restful web service which will use the objects we created above.We will be adding the following methods which can be invoked on this web service.

  • public Response createEmployee(..)
  • public Response deleteEmployee(..)
  • public Employees getAllemployees()
  • public Employee getEmployeeById(..) - Returns XML respsonse.
  • public Employee getEmployeeByJsonId(..) - Returns JSON response
  • public EmployeeService getServiceInfo() - Returns basic info about this webservice
  • public Employee updateEmployee(..)
Frankly we don't need JAXB annotations (@XmlAccessorType and @XmlRootElement), Since getServiceInfo returns some info on this object as XML we added this tags to this class as well.
As you see the comments on each section you will see that each method is self explanatory.
Each method is marked by annotations which indicate the 
  • @Path - The url you need to access this method 
  • @GET,@PUT,@POST - Indicating the HTTP method type you need to use to access the give method
  • @Consumes -  Indicates if the method accepts XML or JSON
  • @Produces - Indicates if the method returns XML or JSON response.
These are key annotations that you need to know to build restful web services.

package com.rama.restful;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
* 
*  To test use this URL 
*  http://localhost:8081/RESTfulDemoApplication/employeeService/employees  ( Your port could be different)
*
**/
//For  fun we can make even this class JAXB enabled to list out the services...
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "employeeService")

@Path("/employeeService")
public class EmployeeService {
 
 @XmlElement(name = "employees")
 private String uri1 = "/employeeService/employees";
 
 
 
 public String getUri1() {
  return uri1;
 }

 public void setUri1(String uri1) {
  this.uri1 = uri1;
 }

 
 
 /**
  * Use the below url to access this
  * http://localhost:8081/RESTfulDemoApplication/employeeService ( Your port could be different)
  * 
  * 
  * OUTPUT: Just prints 
  * 
  * <employeeService>
  *    <employees>/employeeService/employees</employees>
  *    
  * </employeeService>
  * 
  **/
 @GET
 @Path("/")
 @Produces("text/xml;charset=UTF-8")
 public EmployeeService getServiceInfo() {
  return new EmployeeService();
 }

 
 
 
 /**
  * Use the below url to access this
  * http://localhost:8081/RESTfulDemoApplication/employeeService/employees ( Your port could be different)
  * 
  * OUTPUT:
  * 
   *  <employees>
    <employee id="1" uri="/employeeService/employees/1">
     <firstName>Rama</firstName>
     <lastName>chandra</lastName>
    </employee>
    <employee id="2" uri="/employeeService/employees/2">
     <firstName>John</firstName>
     <lastName>Smith</lastName>
    </employee>
   </employees>
  * 
  * 
  * 
  **/
 @GET
 @Path("/employees")
 @Produces("text/xml;charset=UTF-8")
 public Employees getAllemployees() {
  
  //Ideally you will go to database to pull this info..
  
  Employee employee1 = new Employee();
  employee1.setId(1);
  employee1.setFirstName("rama");
  employee1.setLastName("chandra");
  employee1.setUri("/employeeService/employees/1");

  Employee employee2 = new Employee();
  employee2.setId(2);
  employee2.setFirstName("John");
  employee2.setLastName("smith");
  employee2.setUri("/employeeService/employees/2");

  Employees employees = new Employees();
  employees.setEmployees(new ArrayList<Employee>());
  employees.getEmployees().add(employee1);
  employees.getEmployees().add(employee2);

  return employees;
 }

 /**
  * Use the below url to access this
  * http://localhost:8081/RESTfulDemoApplication/employeeService/employees/123 ( Your port could be different)
  * 
  * 
  * OUTPUT:
  *   <employee id="123" uri="/employeeService/employees/123">
   <firstName>demo</firstName>
   <lastName>employee</lastName>
   </employee>
  * 
  **/
 
 @GET
 @Path("/employees/{id}")
 @Produces("text/xml;charset=UTF-8")
 public Employee getEmployeeById(@PathParam("id") int id) {
  
  //Ideally you will go to database to pull this info..
  
  Employee employee = new Employee();
  employee.setId(id);
  employee.setFirstName("demo");
  employee.setLastName("employee");
  employee.setUri("/employeeService/employees/" + id);
  return employee;
 }

 
 
 /**
  * Use the below url to access this
  * http://localhost:8081/RESTfulDemoApplication/employeeService/employeesjson/123 ( Your port could be different)
  * 
  * OUTPUT:
  * 
   {
      "uri": "/employeeService/employees/123",
      "firstName": "demo",
      "lastName": "employee",
      "id": 123
    }

  **/
 @GET
 @Path("/employeesjson/{id}")
 @Produces("application/json")
 public Employee getEmployeeByJsonId(@PathParam("id") int id) {
  
  /* Pretend you are querying data base using this id and returning the results */
  
  Employee employee = new Employee();
  employee.setId(id);
  employee.setFirstName("demo");
  employee.setLastName("employee");
  employee.setUri("/employeeService/employees/" + id);
  return employee;
 }
 
 
 /**
  * Use the below url to access this
  * http://localhost:8081/RESTfulDemoApplication/employeeService/employeesjson2/123 ( Your port could be different)
  * 
  * OUTPUT:
   {
   
       "uri": "/employeeService/employees/123",
       "firstName": "Ram",
       "lastName": "Chandra",
       "id": 123
   }
  * 
  **/
 @GET
 @Path("/employeesjson2/{id}")
 @Produces("application/json")
    public Response getEmployeeById(@PathParam("id") Integer id)
    {
  //Pretend you are querying from database.
        Employee employee = new Employee();
        employee.setId(id);
        employee.setFirstName("Ram");
        employee.setLastName("Chandra");
        employee.setUri("/employeeService/employees/" + id);
        //Another way to send the response
        return Response.status(200).entity(employee).build();
    }
  
 
 /**
  * Use the below url to access this
  * http://localhost:8081/RESTfulDemoApplication/employeeService/employees ( Your port could be different)
  * Make sure you send the following custom http headers.
  * HTTP Headers:
  * =============
  *   Content-Type: text/xml
  *   allow-admin:true
  * 
  * 
  * And in the body the pay load:
  * ===================
  * 
  *    <employee id="1" uri="/employeeService/employees/1">
    <firstName>rama</firstName>
    <lastName>chandra</lastName>
   </employee>

  *  Http-Method:
  *   POST
  *   
  *   I used fire fox rest client add on  for testing.
  **/
 
 @POST
 @Path("/employees")
 @Consumes("text/xml;charset=UTF-8")
 @Produces("text/xml;charset=UTF-8")
 public Response createEmployee(Employee employee,@DefaultValue("false") @QueryParam("allow-admin") boolean allowAdmin)
   throws URISyntaxException {
  System.out.println(employee.getFirstName());
  System.out.println(employee.getLastName());
  return Response.status(201).contentLocation(new URI("/employeeService/employees/123")).build();
 }

 
 /**
  * Use the below url to update (Put method)
  * http://localhost:8081/RESTfulDemoApplication/employeeService/employees ( Your port could be different)
  * 
  *    Input:
  *    <employee id="1" uri="/employeeService/employees/1">
    <firstName>rama</firstName>
    <lastName>chandra</lastName>
   </employee>
  Set HTTP Headers:
  * ===================
  *   Content-Type: text/xml
  * 
  *   HTTP-Method : PUT 
  *   I used fire fox rest client add on  for testing.
  * 
  **/
 @PUT
 // @Path("/employees/{id: [0-9]*}")
 @Path("/employees/{id}")
 @Produces("text/xml;charset=UTF-8")
 @Consumes("text/xml;charset=UTF-8")
 public Employee updateEmployee(@PathParam("id") int id, Employee employee)throws URISyntaxException {
  employee.setId(id);
  employee.setFirstName(employee.getFirstName() + "updated");
  return employee;
 }

 
 
 /*****
  * Use the below url to delete (Delete method)
  * http://localhost:8081/RESTfulDemoApplication/employeeService/employees/1 ( Your port could be different)
  *   where 1 is the id.
  ****/
 @DELETE
 @Path("/employees/{id}")
 public Response deleteEmployee(@PathParam("id") int id)
   throws URISyntaxException {
  System.out.println(" Deleteing employee with id"+ id);
  //Add your magic code to delete 
  return Response.status(200).build();
 }
 
 
}


So how does rest webservice knows that EmployeeService is a Webservice. Well you nee to register that class as shown below.


package com.rama.restful;


import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
 

@ApplicationPath("/")
public class ApplicationConfig extends Application {
    @SuppressWarnings("unchecked")
    public Set<Class<?>> getClasses() {
     List<Class<?>> registeredServiceClassList = Arrays.asList(
       RESTEasyFileService.class,
          EmployeeService.class);
        return new HashSet<Class<?>>(registeredServiceClassList);
    }
}

And in you web.xml add the following



<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
 <display-name>Archetype Created Web Application</display-name>

 <context-param>
  <param-name>resteasy.scan</param-name>
  <param-value>true</param-value>
 </context-param>
 

 <listener>
  <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
 </listener>
 
 <servlet>
  <servlet-name>resteasy-servlet</servlet-name>
  <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
 </servlet>


 <servlet-mapping>
  <servlet-name>resteasy-servlet</servlet-name>
  <url-pattern>/*</url-pattern>
 </servlet-mapping>
</web-app>
This is all you need to set up the basic restful web service.

Now I will show you how to

Upload/Download File using RESTful Web service


In the same package where we added Employeeservice.java , add your RESTEasyFileService.java.
Needless to say you need to register this class with ApplicationConfig .java class as described above.

RESTEasyFileService.java

package com.rama.restful;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;

/**
 * 
 *   A simple class which you can use to upload files 
 *   or down load files..
 *   Source : 
 *     http://examples.javacodegeeks.com/enterprise-java/rest/resteasy/resteasy-file-upload-example/
 *   (Thanks to Nikos Maravitsas for File upload example)
 *     
 */
/**
 * @author twreddy
 *
 */
@Path("/files")
public class RESTEasyFileService {

 private static final String SERVER_UPLOAD_LOCATION_FOLDER = "C://RestFileUploadTest//";
 //Using same folder for testing ...
 private static final String SERVER_DOWNLOAD_LOCATION_FOLDER = "C://RestFileUploadTest//";  
 
 
 @GET
 @Path("/")
 @Produces("text/xml;charset=UTF-8")
 public String getServiceInfo() {
  return "<services>/upload" +
    "  For Upload use : http://localhost:8081/RESTfulDemoApplication/files/upload     Your port may differ \n" +
    "  For downloads use : http://localhost:8081/RESTfulDemoApplication/files/download/SpringAnnontationsCheatSheet.pdf  \n" +
    "  where SpringAnnontationsCheatSheet.pdf is the file you have on your server at SERVER_DOWNLOAD_LOCATION_FOLDER . You can use a different file \n" +
    "</services>";
 }
 
 
 
 /**
  *  Method that handles file Upload.
  *  
  *  Use sample html page to upload . The main content of the html page should look like this 
  *  
  *  
   <form action="http://localhost:8081/RESTfulDemoApplication/files/upload" method="post" enctype="multipart/form-data">
    <p>
     Select a file : <input type="file" name="file" size="50" />
    </p>
    <input type="submit" value="Upload It" />
   </form>
 
  *  
  **/
 @POST
 @Path("/upload")
 @Consumes("multipart/form-data")
 public Response uploadFile(MultipartFormDataInput input) {

  String fileName = "";

  Map<String, List<InputPart>> formParts = input.getFormDataMap();

  List<InputPart> inPart = formParts.get("file");

  for (InputPart inputPart : inPart) {

   try {

    // Retrieve headers, read the Content-Disposition header to
    // obtain the original name of the file
    MultivaluedMap<String, String> headers = inputPart.getHeaders();
    fileName = parseFileName(headers);
    // Handle the body of that part with an InputStream
    InputStream istream = inputPart.getBody(InputStream.class, null);
    fileName = SERVER_UPLOAD_LOCATION_FOLDER + fileName;
    saveFile(istream, fileName);

   } catch (IOException e) {
    e.printStackTrace();
   }

  }

  String output = "File saved to server location : " + fileName;

  return Response.status(200).entity(output).build();
 }

 
  
 /**
  *
  * http://localhost:8081/RESTfulDemoApplication/files/download/Test.pdf 
  * Where Test.pdf is the document I have stored under SERVER_DOWNLOAD_LOCATION_FOLDER path.
  * 
  */
 @GET
 @Path("/download/{fileName}")
 @Produces(MediaType.APPLICATION_OCTET_STREAM)
 public Response getFile(@PathParam("fileName") String fileName) {
     File file = new File(SERVER_DOWNLOAD_LOCATION_FOLDER+fileName);
     ResponseBuilder response = Response.ok((Object) file);
     response.header("Content-Disposition","attachment; filename="+fileName);
      return response.build();

 }
 
 
 /*
  *  Content-Disposition: form-data; name="file"; filename="AVD1.png"
  Content-Type: image/png

  */
 // Parse Content-Disposition header to get the original file name
 private String parseFileName(MultivaluedMap<String, String> headers) {
  String[] contentDispositionHeader = headers.getFirst("Content-Disposition").split(";");
  for (String name : contentDispositionHeader) {
   if ((name.trim().startsWith("filename"))) {
    String[] tmp = name.split("=");
    String fileName = tmp[1].trim().replaceAll("\"", "");
    return fileName;
   }
  }
  return "defaultFileName";
 }

 // save uploaded file to a defined location on the server
 private void saveFile(InputStream uploadedInputStream, String serverLocation) {

  try {
   OutputStream outpuStream = new FileOutputStream(new File(
     serverLocation));
   int read = 0;
   byte[] bytes = new byte[1024];

   outpuStream = new FileOutputStream(new File(serverLocation));
   while ((read = uploadedInputStream.read(bytes)) != -1) {
    outpuStream.write(bytes, 0, read);
   }
   outpuStream.flush();
   outpuStream.close();
  } catch (IOException e) {

   e.printStackTrace();
  }
 }
}

So this code now can handle both upload and download.

You can download the above server side Restful web service code at 


Rest easy web service clients

In your client package better to copy Employee.java before you add this client code.

Pure Java Restful Web service  Client

This code shows a simple java client that invokes getEmployee method  and prints the XML.

package com.rama.rest.ws.client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import com.rama.restful.Employee;


/**
 * 
 * Pure java client example to test EmployeeService
 * @author twreddy
 *
 */
public class PureJavaClient  {
 public static void main(String[] args)throws Exception {
  try {
   getEmployeeByIDAsXML();

  } catch (MalformedURLException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  } 
 }
 
  
 private static void getEmployeeByIDAsXML()throws ProtocolException, IOException, JAXBException {
  
  // Not really a good idea to openconnection for each Request...
  URL url = new URL("http://localhost:8081/RESTfulDemoApplication/employeeService/employees/1");
  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  
  conn.setRequestProperty("Content-Type", "text/plain; charset=utf-8");
  conn.setRequestMethod("GET");
  
  if (conn.getResponseCode() != 200) {
   throw new RuntimeException("Failed : HTTP error code : "+ conn.getResponseCode());
  }

  BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
  String responseStr = br.readLine();
  System.out.println("Response :"+responseStr);
  conn.disconnect();

  JAXBContext jaxbContext = JAXBContext.newInstance(Employee.class);
  Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
  Employee user = (Employee) jaxbUnmarshaller.unmarshal(new StringReader(responseStr));

  System.out.println(user.getId());
  System.out.println(user.getFirstName());
  System.out.println(user.getLastName());
 }
}

Jboss rest easy client

Here is the Jboss rest easy client API. Here all you need is to define an interface which more or less matches you webservice method signatures and then create Proxy using the Interface.
Don't worry once you see the below code it will be more clear.
More info @ 


Lets call the Rest easy client for EmployeeService as EmployeeServiceRestfulClient.java 

package com.rama.rest.ws.client;

import java.net.URISyntaxException;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

/**
 * 
 * Restful Client  which will be used by 
 * org.jboss.resteasy.client.ProxyFactory.
 * 
 * See  RestfulClientTest.java to see how to use this class.
 * 
 * BASEURL : http://localhost:8081/RESTfulDemoApplication/employeeService
 *   (your port may be different)
 * @author twreddy
 *
 */
public interface EmployeeServiceRestfulClient {

  @GET
     @Path("/")
     @Produces("text/xml;charset=UTF-8")
     String getServiceInfo();
  
  @GET
  @Path("/employees")
  @Produces("text/xml;charset=UTF-8")
  public String getAllemployees();
  
  
 @GET
 @Path("/employees/{id}")
 @Produces("text/xml;charset=UTF-8")
 public String getEmployeeById(@PathParam("id") int id);
 
 
 @GET
 @Path("/employeesjson/{id}")
 @Produces("application/json")
 public String getEmployeeByJsonId(@PathParam("id") int id);
 
 
 @GET
 @Path("/employeesjson2/{id}")
 @Produces("application/json")
    public Response getEmployeeById(@PathParam("id") Integer id);
 
 
 @POST
 @Path("/employees")
 @Consumes("text/xml;charset=UTF-8")
 @Produces("text/xml;charset=UTF-8")
 //Notice that we are sending Employee object as XML pay load.
 public Response createEmployee(String employee,@DefaultValue("false") @QueryParam("allow-admin") boolean allowAdmin)
   throws URISyntaxException;
 
 
 @PUT
 // @Path("/employees/{id: [0-9]*}")
 @Path("/employees/{id}")
 @Produces("text/xml;charset=UTF-8")
 @Consumes("text/xml;charset=UTF-8")
 public String updateEmployee(@PathParam("id") int id, String employee);
 
 @DELETE
 @Path("/employees/{id}")
 public Response deleteEmployee(@PathParam("id") int id);
 
}

Lets call the Rest easy client for RESTEasyFileService as RestEasyFileServiceRestfulClient.java 
package com.rama.rest.ws.client;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;

/**
 * @author twreddy
 *  
 *  
 * Restful Client  which will be used by 
 * org.jboss.resteasy.client.ProxyFactory.
 * 
 * See  RestfulClientTest.java to see how to use this class.
 * 
 *  BASE URL: 
 *  http://localhost:8081/RESTfulDemoApplication/files
 */
public interface RestEasyFileServiceRestfulClient {

 
 @GET
 @Path("/")
 @Produces("text/xml;charset=UTF-8")
 public String getServiceInfo();
 
 
 @POST
 @Path("/upload")
 @Consumes("multipart/form-data")
 public Response uploadFile(MultipartFormDataOutput  outPut);
 
 @GET
 @Path("/download/{fileName}")
 @Produces(MediaType.APPLICATION_OCTET_STREAM)
 public Response getFile(@PathParam("fileName") String fileName);
 
}
As you see we hardly have written any code to create the restful rest easy clients. 
Now lets make use of the above Restful clients as shown below.
I added both EmployeeServiceRestfulClient.java and RestEasyFileServiceRestfulClient.java   into same test class.Feel free to have separate testing code for each of them.

RestfulClientTest.java

The key in this class is the line which creates PROXY 
EmployeeServiceRestfulClient client = ProxyFactory.create(EmployeeServiceRestfulClient.class,baseUrl);
I have commented some code intentionally while testing. You can comment or uncomment whichever section you need.

package com.rama.rest.ws.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.jboss.resteasy.client.ProxyFactory;
import org.jboss.resteasy.client.core.BaseClientResponse;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;

/**
 * @author twreddy
 *
 * Comment or un comment which ever section you need for testing 
 */
public class RestfulClientTest {

 public static void main(String[] args) throws Exception {
  
  //Employee Service Test 
  String empServiceBaseUrl = "http://localhost:8081/RESTfulDemoApplication/employeeService";
  employeeServiceTest(empServiceBaseUrl);
  
  
  
  //RESTEasyFileService test 
  String fileServiceUrl = "http://localhost:8081/RESTfulDemoApplication/files";
  fileServiceTest(fileServiceUrl);
  
  
  
 }

 

 /**
  *  Employee Service Test . Comment or un comment which ever section you want to test.
  */
 private static void employeeServiceTest(String baseUrl ) {
  //NOTE: Dont re use the same client for different calls. 
  EmployeeServiceRestfulClient client = ProxyFactory.create(EmployeeServiceRestfulClient.class,baseUrl);
  
  /*  Get Employee test 
   
  String response = client.getEmployeeByJsonId(1);
  System.out.println("Response:" + response);
  
  Response responseObj = client.getEmployeeById(new Integer(10));
  System.out.println("Get by id="+ responseObj.getStatus());
  
  */
  
  
  /*  Create Employee  test 
   
  String empStr = "<employee id=\"1\" uri=\"/employeeService/employees/1\"><firstName>rama</firstName><lastName>chandra</lastName></employee>";
  Response responseObjForCreateEmp = client.createEmployee(empStr,true);
  System.out.println("Create Emp ="+responseObjForCreateEmp.getStatus());
  
  */
  
  
  /*  Update Employee test
  
  String empStr = "<employee id=\"1\" uri=\"/employeeService/employees/1\"><firstName>rama</firstName><lastName>chandra</lastName></employee>";
  String updatedEmpStr = client.updateEmployee(1,empStr);
  System.out.println("updatedEmpStr Emp ="+updatedEmpStr);
  
  */
  
  /* Delete Employee test */
  
  Response  responseObj = client.deleteEmployee(1);
  System.out.println("Deleted  Emp  status="+responseObj.getStatus());
 }

 
 
 
 /**
  * 
  * @param fileServiceUrl
  * @throws FileNotFoundException
  * @throws IOException
  */
 private static void fileServiceTest(String fileServiceUrl)
   throws FileNotFoundException, IOException {
  
  
  
  /*  File upload  Test  */
  /* 
   RestEasyFileServiceRestfulClient fileServiceClient = ProxyFactory.create(RestEasyFileServiceRestfulClient.class,fileServiceUrl);  
   MultipartFormDataOutput mdo = new MultipartFormDataOutput();
      mdo.addFormData("file", new FileInputStream(new File("c:\\Test.pdf")),MediaType.APPLICATION_OCTET_STREAM_TYPE);
      Response r = fileServiceClient.uploadFile(mdo);
      System.out.println("File upload Response="+ r.getStatus() +":"+ r.getMetadata());
    */
  
             
             
  /*  To Download a File...
   */
  RestEasyFileServiceRestfulClient fileServiceClient = ProxyFactory.create(RestEasyFileServiceRestfulClient.class,fileServiceUrl);
  BaseClientResponse response = (BaseClientResponse)fileServiceClient.getFile("ASD.zip");
  //You can get whatever  name the server sent
  System.out.println(response.getHeaders().getFirst("Content-Disposition"));  
  File s = (File)response.getEntity(File.class);
  File ff = new File("C:\\ASD.zip");
     s.renameTo(ff);
     FileWriter fr = new FileWriter(s);
     fr.flush();
  System.out.println("FileDownload Response = "+ response.getStatus());
 }
 
 
}

A simple HMTL client for testing File upload


<html>
<head>
<title>Form Page</title>
</head>
<body>
 <h1>Upload a File</h1>

 <form action="http://localhost:8081/RESTfulDemoApplication/files/upload " method="post" enctype="multipart/form-data">
  <p>
   Select a file : <input type="file" name="file" size="50" />
  </p>
  <input type="submit" value="Upload It" />
 </form>

</body>
</html>

FIREFOX Restful Webservice Client:

Now I will show you some screen shots as how you can test restful web services with just firefox rest client .

1. CreateEmployee - This is how you can test createEmployee.You need to add two http headers
allow-admin:true
Content-Type:text/xml
and choose the method as POST as shown.



2. UpdateEmployee - Choose method as "PUT" and http header as "Content-Type:text/xml"



3. GetEmployee 



4.  FileDownload - Just type the full url in browser ( http://localhost:8081/RESTfulDemoApplication/files/download/SpringAnnontationsCheatSheet.pdf )
where "SpringAnnontationsCheatSheet.pdf" was the document that was already present on the server


Hope this article gives you enough info for writing restful webcservices and testing them with different clients.

You can download the client code at  RestFullWebserviceClients.zip
Leave your comments if you have any below.

Sunday, July 06, 2014

How to print documents using Java ? How to use PJL or Ghostscript with Java for Printing ?

In this article we will be discussing the below listed topics.
  • How to print documents using Java ?
  • How to use PJL (Print job language) commands to send instructions to printer ?
  • How to use Ghostscript commands to send instructions to printer ?
  • How to staple or duplex print documents using printer in Java ?
  • Print pdf's using java.
  • How to check the printer properties using java ?
  • How to select media tray using java on the printer ?
  • How to staple documents on a printer using java code ?
  • Print PDF using PrinterJob in java
  • Print pdf in network printer using java
  • java printing - printing a pdf 
  • Using Java to Print PDF Documents
  • Printing PDF files from Java
  • How to print PDF files using java print API ? 
  • Use Printing Service in Java.
  • Working with Print Services and Attributes.


I had to work on  printing pdf documents and had some special requirements where in few documents were supposed to use one type of paper 
compared to other set of documents. Basically we wanted to control Tray selection during printing.
The plain java api though has commands to set printer trays I guess the driver did not support or am not sure of the exact reason but I couldn't do it within java.
So had to use some PJL commands and Ghost script to get this done with rest of the stuff in JAVA


Here is some sample code Which Prints the media trays for Default printer or can read a different printer as input and prints its attributes. Then displays all tray available and will ask user the tray to select Then will print an empty page for testing targeting that Tray number..


package com.rama.print.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.SimpleDoc;
import javax.print.attribute.AttributeSet;
import javax.print.attribute.HashAttributeSet;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.MediaTray;
import javax.print.attribute.standard.PrinterName;

/**
 * @author TWreddy
 *
 * Prints the media trays for Default printer or can read a different printer as input and prints its attributes. 
 * Then display all tray available and will ask user the tray to select 
 * Then will print an empty page for testing targeting that Tray number..
 */
public class TestMyPrinter   {
 public static void main(String args[])throws Exception {
  printAllPrinters();
  System.out.println(" -----------------------------------------------------------");
  System.out.println(" Above is the list of printers accessible from your machine.");
  System.out.println(" -----------------------------------------------------------");
  System.out.println(" ");
  System.out.println(" ");
  // get default printer
  PrintService defaultPrintService = PrintServiceLookup.lookupDefaultPrintService();

  // suggest the use of the default printer
  
  System.out.println("Enter the printer name or press enter for default printer which is: ["+ defaultPrintService.getName() + "]?" );
  System.out.println("");
  
  // read from the console the name of the printer
   BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in));
   String printerName = bufferRead.readLine();

  
  // if there is no input, use the default printer
  if (printerName == null || printerName.equals("")) {
   printerName = defaultPrintService.getName();
  }

  // the printer is selected
  AttributeSet aset = new HashAttributeSet();
  aset.add(new PrinterName(printerName, null));
  // selection of all print services
  PrintService[] services =            PrintServiceLookup.lookupPrintServices(null,aset);
  Map<Integer, Media> trayMap = getAvailableTraysOnPrinter(services);

  
  System.out.println("Select tray target id : " +"\n");
  String mediaId = bufferRead.readLine();

  MediaTray selectedTray = (MediaTray) trayMap.get(Integer.valueOf(mediaId));
  System.out.println("Selected tray : " + selectedTray.toString() +"\n");

  System.out.println("Do you want to print a test page? [y/n] \n");
  
  String printPage = bufferRead.readLine();
  
  if (printPage.equalsIgnoreCase("Y")) {
   printDoc(services, selectedTray);
  }
  
  System.out.println(" Completed.");
 }

 /**
  *  Prints an empty document based on Tray you selected.
  * or you can print a pdf 
  * or you can output the contents that was sent to printer to another file for debugging.
         *  Comment or uncomment the code below depending on whether you want to test prniting 
         *  empty document (useful to test printer and  you dont want to waste paper)
         *  or print a actual document or want to see what content is exactly being sent to printer.
  * @param services
  * @param selectedTray
  */
 private static void printDoc(PrintService[] services,MediaTray selectedTray) {
  // we have to add the MediaTray selected as attribute
  

  
  
  // we create the printer job, it print a specified document with a
  // set of job attributes
  //DocPrintJob printJob = filePrinter.getPrintService().createPrintJob();
  DocPrintJob job = services[0].createPrintJob();

  
  try {
   System.out.println("Trying to print an empty page (Or uncomment code to Print PDF) on Media Tray: "+ selectedTray.toString());
   
   System.out.println(" Media tray selection only works for documents thats created on the fly . See PrintableDemo class below.");
   System.out.println(" For documents like pdf, word, text files...etc... I had to use Ghostscript and to that document add @PJL commands.");
   
   
   
   // we print using the selected attributes which is paper tray.
   //SET PRINTER PROPERTIES
   /* This approach works only for CASE1 and CASE3  described below . */
   PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
   attributes.add(selectedTray);
   
   
   
 //CASE1: SIMPLE TEST where we try to print a document created on the fly with a simple String
 // we create a document that implements the printable interface . 
 // I just passed selectedTray so that if required print that on the paper for testing.
  Doc doc = new SimpleDoc(new PrintableDemo(selectedTray),DocFlavor.SERVICE_FORMATTED.PRINTABLE, null);
   
   
   
   
 //CASE2: TEST with actual documents . Here the tray selection doesnt work.
 /*  Somehow when I am using a PDF though I select tray 2 its sending to Tray1.
    But this same code when invoked with empty doc works ???
 NOTE: After quite testing I noticed that when we print a document the entire binary is sent 
   Directly to printer with no PCL commands for tray info or stapling etc... 
   In case of
 */
   
   //InputStream inputStream = new FileInputStream("C://coverPage.pdf");
   //InputStream inputStream = new FileInputStream("C://other.pdf");
   //InputStream inputStream = new FileInputStream("C://other.docx");
   //Doc doc = new SimpleDoc(inputStream, DocFlavor.INPUT_STREAM.AUTOSENSE ,null);
   
   
   
 //CASE3: Use ghost script and modify the pdf to ps file and to that add any PCL commands.
 //So that you can control tray selection , duplex printing, stapling and what not..
 //File psFile = modifyDocumentUsingGhostScriptAndPrint( new File("C://coverPage.pdf"));
 //InputStream inputStream = new FileInputStream(psFile);
 //Doc doc = new SimpleDoc(inputStream, DocFlavor.INPUT_STREAM.AUTOSENSE ,null);
 
   
  /** 
   * Want to see what you really sent to printer ??
   * 
   * PRINT TO FILE
   * You want to see what exactly is being sent to printer un comment this code
   * and it will write the entire Stream to a flie (which is want Printer would have received) 
  **/
   //File outputFile = new File("C://ContentSentToPrinter.txt");
   //attributes.add(new Destination(outputFile.toURI()));
   
   
   job.print(doc, attributes);
   
   System.out.println(" Done processing ......");
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

/**
  *  Transforms the PDF file to Post script 
  *  and as a demo adds 
  *  - PJL commands to Print a particular tray
  *  - Adds PS commands to staple or duplex print.
  *  
  *   Bottom line you can add much more stuff... Just showing what all can be done..
  *   
  */
 public static File modifyDocumentUsingGhostScriptAndPrint(File pdfFile)throws Exception{
  File psFile = null;
  psFile = PostScriptConversion.convertToPS(pdfFile);
  PostScriptConversion.addAddtionalCommandsForPSFile(psFile);
  if(psFile == null){
   System.err.println(" Oops PS conversion didnt yeild us anything.");
   throw new RuntimeException(" Oops PS conversion didnt yeild us anything.");
  }
  return psFile; 
 } 
 
 
 
 
 private static Map<Integer, Media> getAvailableTraysOnPrinter(PrintService[] services) {
  // We store all the trays in a map
  Map<Integer, Media> trayMap = new HashMap<Integer, Media>(10);

  //We chose something compatible with the printable interface
  DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
  //

  for (PrintService service : services) {
   System.out.println(service);

   // we retrieve all the supported attributes of type Media
   // we can receive MediaTray, MediaSizeName, ...
   Object o = service.getSupportedAttributeValues(Media.class, flavor, null);
   if (o != null && o.getClass().isArray()) {
    for (Media media : (Media[]) o) {
     // we collect the MediaTray available
     if (media instanceof MediaTray) {
      System.out.println(" Use id number :"+ media.getValue() + "  For accessing: " + media + " - " + media.getClass().getName());
      trayMap.put(media.getValue(), media);
     }
    }
   }
  }
  return trayMap;
 }
 
 
 private static void printAllPrinters() {
  PrintRequestAttributeSet printRequestAttributes = new HashPrintRequestAttributeSet();
   //  printRequestAttributes.add(OrientationRequested.LANDSCAPE);
   //  printRequestAttributes.add(Sides.DUPLEX);
   //  printRequestAttributes.add(MediaSizeName.ISO_A4);
   //     printRequestAttributes.add(Finishings.STAPLE);
  PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, printRequestAttributes);
  for(PrintService printService :printServices){
   System.out.println(printService +" Name to use="+ printService.getName());
  }
 }
 
 
 private static void printPrinterProperties(String printName){
  AttributeSet aset = new HashAttributeSet();
  aset.add(new PrinterName(printName, null));
  PrintService[] services = PrintServiceLookup.lookupPrintServices(null, aset);
  for (int i = 0; i < services.length; i++) {
    PrintService service = services[i];
    System.out.println(service);
    //DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PAGEABLE;
    DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
    Object attributes = service.getSupportedAttributeValues(Media.class, flavor, null);
    if (attributes != null && attributes.getClass().isArray()) {
      for (Media media : (Media[]) attributes) {
        System.out.println(media + " ID: " + media.getValue() + "\t" + media.getClass().getName());
        /* if(media instanceof sun.print.Win32MediaTray){
         Win32MediaTray  win32Tray = (Win32MediaTray)media;
         System.out.println(win32Tray.winID);
        }*/
        
      }
    }
  }
  
 }
 
}
Here is the printable demo class that you can use to print empty document. This will be usefull when testing printer and tray selection within the printer that way you can you same sheet of paper again and again.

package com.rama.print.test;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;

import javax.print.attribute.standard.MediaTray;
/**
 * 
 * To print a page just on the fly without reading physical document.
 * good for testing 
 * @author TWreddy
 *
 */
public class PrintableDemo implements Printable {
 
 MediaTray selectedTray = null;
 public PrintableDemo(MediaTray trayToUse){
  selectedTray = trayToUse;
 }
 
 
 @Override
 public int print(Graphics pg, PageFormat pf, int pageNum) {
  
  //The printing system will call the Printable.print()  until we return NO_SUCH_PAGE
  if (pageNum > 0){
   //First time pageNum = 0...
   System.out.println("PrintableDemo.print pageNum="+ pageNum);
   return Printable.NO_SUCH_PAGE;
  }
  
  
  //User (0,0) is typically outside the imageable area, so we must translate
     // by the X and Y values in the PageFormat  to avoid clipping.
     Graphics2D g2d = (Graphics2D)pg;
     g2d.translate(pf.getImageableX(), pf.getImageableY());

     
     //For testing SAVE paper by setting messagOnDoc=""; that way only empty paper will get pulled from Tray with nothing being printed.   
     String messagOnDoc = "Hello  Your tray number was="+ this.selectedTray.getName() +" : "+this.selectedTray.getValue();
     // Now we perform our rendering
     pg.drawString(messagOnDoc, 100, 100);

     // tell the caller that this page is part
     // of the printed document
     return PAGE_EXISTS;
     
 }
}

Here is the code for Converting a PDF document to PostScript.
And in addition we can additional post script commands and also PJL commands
eg: for Duplex printing, Stapling of documents if printer supports.
The assumption is you Ghost script installed on your local.

package com.rama.print.test;

import java.io.BufferedReader;
import java.io.EOFException;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.PrintWriter;

import org.apache.commons.io.FileUtils;

/**
 * @author TWreddy
 * Convert a PDF document to PostScript.
 * And in addtion we can additional post script commands and also PJL commands 
  * eg: for Duplex printing, Stapling of documents if printer supports.
 */
class PostScriptConversion extends Thread {
 
 //NOTE: To see how PJL looks print a document (withou ot java code)  and while doing 
 //Say print to file (its that check box on the print properties window). 
 // Which means you are exporting the entire document with all PJL commands 
 // that would have been sent to printer otherwise. 
 
 //ESC%-12345X@PJL is start of print job language.
 private static String PJL_START = String.valueOf((char) 27) + "%-12345X@PJL";
 private static String PJL_END = String.valueOf((char) 27) + "%-12345X";
 private static String PJL_POSTSCRIPT = "@PJL ENTER LANGUAGE = POSTSCRIPT";
 private static String PJL_COMMAND_TO_SELECT_PRINT_TRAY = "@PJL SET MEDIASOURCE = TRAY2";
 
 private static String POST_SCRIPT_COMMAND_FOR_DUPLEX_PRINTING = "<< /Duplex true >> setpagedevice";
  
 
 boolean isRunning;
 String command = null;
 Process process = null;
 int exitVal = -1;
 boolean processingComplete;
 
 public PostScriptConversion( String command) {
  this.command = command;
 }

 public void run() {
  try {
   System.out.println("executing: " + command);
   process = Runtime.getRuntime().exec(command);

   // Spawn thread to read output of spawned program  --  
   new Thread() {
    public void run() {
     this.setName("Process-InputStream");
     // hook into output from spawned program
     final InputStream is = process.getInputStream();
     final InputStreamReader isr = new InputStreamReader(is);
     final BufferedReader br = new BufferedReader(isr, 100);
     String line;
     try {
      try {
       while ((line = br.readLine()) != null) {
        System.out.println(line);
       }
      } catch (EOFException e) {
      }
      br.close();
     } catch (IOException e) {
      System.err.println("problem reading spawn output" + e.getMessage());
     }
     // returning from run kills the thread.
    }
   }.start();

   // Spawn thread to read output of spawned program -- Error Stream
   new Thread() {
    public void run() {
     this.setName("Process-ErrorStream");
     // hook into output from spawned program
     final InputStream is = process.getErrorStream();
     final InputStreamReader isr = new InputStreamReader(is);
     final BufferedReader br = new BufferedReader(isr, 100);
     String line;
     try {
      try {
       while ((line = br.readLine()) != null) {
        System.out.println(line);
       }
      } catch (EOFException e) {
      }
      br.close();
     } catch (IOException e) {
      System.err.println("problem reading spawn output" + e.getMessage());
     }
     // returning from run kills the thread.
    }
   }.start();

   // Wait for Ghost Script to complete.
   System.out.println("waiting for command to finish ....");
   exitVal = process.waitFor();
   System.out.println("exitValue: " + exitVal);

   processingComplete = true;
   process = null;
   System.out.println("PostConversion.run(): Done converting ");
   
  } catch (Exception e) {
   System.err.println("incomplete:"+ e);
  }
 }
 
 
 
 public static File convertToPS(File pdfFile) {
  String pdfFilePath = pdfFile.getAbsolutePath();
  String pdfFileName = pdfFile.getName();
  String psFileName = pdfFileName.substring(0, pdfFileName.indexOf(".pdf")) + ".ps";
  String psFilePath = "C:\\Temp\\"+ "\\" + psFileName;

  StringBuffer command = new StringBuffer("C:/Progra~1/gs/gs8.61/bin/gswin32c.exe");
  command.append(" -q -sOutputFile#");
  command.append("\"");
  command.append(psFilePath);
  command.append("\"");
  command.append(" -dNOPAUSE -dBATCH -dSAFER -sDEVICE=pswrite ");

  command.append("\"");
  command.append(pdfFilePath);
  command.append("\"");
  
  try {

   PostScriptConversion conversionThread = new PostScriptConversion(command.toString());
   conversionThread.setName("PostConversion");
   conversionThread.start();
   try {
    //Set timeout for PS conversion. Best is to externalize.. for testing read directly.
    Thread.sleep(100000);
   } catch (Exception ex) {
    ex.printStackTrace();
   }

   if (!conversionThread.processingComplete) {
    if (conversionThread.process != null) {
     File psFile = new File(psFilePath);
     if (psFile.exists()) {
      conversionThread.process.destroy();
      conversionThread.process = null;
      System.err.println("Killed process.");
     }
    }
   }

   System.out.println(" Return value from PS conversion " + conversionThread.exitVal);

   if (conversionThread.exitVal != 0) {
    return null;
   }

   return new File(psFilePath);
  } catch (Exception e) {
   System.err.println("Error converting to PS: " + psFilePath +" ExP:"+ e);
  }
  return null;
 }
 
 
 
 
 /**
  * 
  * This method adds Printer Job Language (PJL) as well as postscript commands to achieve desired finishing on the final output.
  * 
  * PJL commands will follow the structure
  * 
  * 1. PJL directive to indicate this is a PJL command stream 
  * 2. Any number of PJL commands there after  
  * 3. PJL command to set language to postscript eg: @PJL ENTER LANGUAGE = POSTSCRIPT or  @PJL ENTER LANGUAGE = PCLXL 
  * 4. the actual postscript with any additional  modifications 
  * 5. PJL directive to indicate the end of the PJL job
  * 
  * so basically we are adding PJL commands on top and embedding the postscrip output which is the 
  * original pdf  wrapped between PJL start and end Commands.
  * 
  * @param psFile
  * @throws IOException
  */
 public static void addAddtionalCommandsForPSFile(File psFile) throws IOException {

  File tempPSFile = new File(psFile.getPath() + "_upd");
  PrintWriter pw = null;
  FileReader fr = null;
  try {
   pw = new PrintWriter(tempPSFile);
   fr = new FileReader(psFile);

   // Begin the PJL job
   pw.println(PJL_START);
   //ANY PJL commands will go here 
   pw.println(PJL_COMMAND_TO_SELECT_PRINT_TRAY);
   //pw.println();  More PJL commands.
   //pw.println();  More PJL commands.
   //set the language back to postscript
   pw.println(PJL_POSTSCRIPT);
   
   //Now read inout Post script file to modify any commands on the fly as we read.
   LineNumberReader lnr = new LineNumberReader(fr);
   String line = lnr.readLine();
   pw.println(line); //copy  the first line from PS file...
   pw.println(POST_SCRIPT_COMMAND_FOR_DUPLEX_PRINTING); //Add Post Script command for duplex prining
   //pw.println(); //More PS commands (NOT PJL)
   
   line = lnr.readLine();
   int endPageFindCount = 0;
   int scaleFindCount = 0;
   while (line != null) {
    if (line.equalsIgnoreCase("%%EndPageSetup")) { //Count each page if you want...
     endPageFindCount++;
     pw.println("0 100 translate"); 
     pw.println(line);
    } else if (line.equalsIgnoreCase("0.1 0.1 scale")) {
     scaleFindCount++;
     pw.println("0.1 0.099 scale"); //Adjust scaling in Post script if you want. 
    } else {
     pw.println(line); //else just copy to the other file.
    }
    line = lnr.readLine();
   }

   // end the PJL job
   pw.println(PJL_END);

   System.out.println("Processing PS results: endPageFindCount: " + endPageFindCount + "  scaleFindCount: " + scaleFindCount);

  } catch (Exception e) {
   // TODO: handle exception
   System.err.println(e.getMessage()+":" +e);
  } finally {
   if (pw != null) {
    pw.close();
   }
   if (fr != null) {
    fr.close();
   }
  }

  FileUtils.forceDelete(psFile);
  FileUtils.copyFile(tempPSFile, psFile);
  FileUtils.forceDelete(tempPSFile);

 }
}

This above code should provide you enough  java code to print all print services on any given machine, modify a document to add any ghost script commands or PJL commands to control printer in a way at times we may not be able to do using Pure java code.