de.fun2code.google.cloudprint.CloudPrintConnection.java Source code

Java tutorial

Introduction

Here is the source code for de.fun2code.google.cloudprint.CloudPrintConnection.java

Source

package de.fun2code.google.cloudprint;

/*
 Copyright 2011 Jochen Ruehl
    
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
    
 http://www.apache.org/licenses/LICENSE-2.0
    
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Implementation of the Google Cloud Print (GCP) API as described at the Google
 * Cloud Print page.<br>
 * The implementation might be not complete but should basically work.
 * 
 * @see <a
 *      href="http://code.google.com/intl/de-DE/apis/cloudprint/docs/overview.html">Google
 *      Cloud Print - Overview</a>
 * 
 * @author Jochen Ruehl
 * 
 */
public class CloudPrintConnection {
    private static String GOOGLE_CLIENT_LOGIN_URL = "https://www.google.com/accounts/ClientLogin";

    private static String GCP_BASE_URL = "https://www.google.com/cloudprint/interface";

    private static String GOOGLE_AUTH_TOKEN_PREFIX = "Auth=";

    private static String PPD_MINIMAL = "*PPD-Adobe: \"4.3\"";

    private static String GCP_CMD_REGISTER = "register";

    private static String GCP_CMD_UPDATE = "update";

    private static String GCP_CMD_DELETE = "delete";

    private static String GCP_CMD_CONTROL = "control";

    private static String GCP_CMD_LIST = "list";

    private static String GCP_CMD_SEARCH = "search";

    private static String GCP_CMD_FETCH = "fetch";

    private static String GCP_CMD_DELETE_JOB = "deletejob";

    private static String GCP_CMD_SUBMIT = "submit";

    private static String GCP_CMD_JOBS = "jobs";

    private static String GCP_CMD_PRINTER = "printer";

    public static String JOB_STATUS_QUEUED = "QUEUED";

    public static String JOB_STATUS_IN_PROGRESS = "IN_PROGRESS";

    public static String JOB_STATUS_DONE = "DONE";

    public static String JOB_STATUS_ERROR = "ERROR";

    private HttpClient httpclient;

    private String authToken;

    private String email;

    private String passwd;

    private String service;

    private String source;

    private Response lastResponse = new Response();

    private HttpPost currentPost;

    private HttpGet currentGet;

    private HttpHead currentHead;

    /**
     * Connection method that describes the basic credentials needed to receive
     * a Google Auth Token.
     * 
     * @param email
     *            Google account email.
     * @param passwd
     *            Google account password.
     * @param service
     *            Description of Google service used.
     * @param source
     *            Requestor, basically a string that describes the application
     *            version etc..
     * @param httpclient
     *            HTTP Client (optional). A HTTP Client can be passed if special
     *            proxy. settings are needed, if this parameter is <tt>null</tt>
     *            a new HTTP Client will be constructed.
     * @return <tt>true</tt> if a Google Auth Token could be fetched,
     *         otherwise <tt>false</tt>.
     */
    public boolean connect(String email, String passwd, String service, String source, HttpClient httpclient) {
        this.email = email;
        this.passwd = passwd;
        this.service = service;
        this.source = source;

        if (httpclient != null) {
            this.httpclient = httpclient;
        } else {
            this.httpclient = new DefaultHttpClient();
        }

        return refreshAuthToken();
    }

    /**
     * Connection method that connects with the provided Google Auth Token.
     * 
     * @param authToken
     *            The Google Auth Token.
     * @param httpclient
     *            HTTP Client (optional).
     */
    public void connect(String authToken, HttpClient httpclient) {
        this.email = null;
        this.passwd = null;
        this.service = null;
        this.source = null;

        this.authToken = authToken;

        if (httpclient != null) {
            this.httpclient = httpclient;
        } else {
            this.httpclient = new DefaultHttpClient();
        }
    }

    /**
     * Shuts down the HTTP Client connection. Should be called on application
     * exit.
     */
    public void disconnect() {
        httpclient.getConnectionManager().shutdown();
    }

    /**
     * Gets the last response of the Cloud Print call made.
     * 
     * @return Last response information.
     */
    public Response getLastResponse() {
        return lastResponse;
    }

    /**
     * Retrieves a Googel Auth Token
     * 
     * @return Auth Token or <tt>null</tt> if no token could be fetched.
     * @throws ClientProtocolException
     * @throws IOException
     */
    private String fetchGoogleAuthToken() throws ClientProtocolException, IOException {
        HttpPost post = new HttpPost(GOOGLE_CLIENT_LOGIN_URL);

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        nvps.add(new BasicNameValuePair("Email", email));
        nvps.add(new BasicNameValuePair("Passwd", passwd));
        nvps.add(new BasicNameValuePair("service", service));
        nvps.add(new BasicNameValuePair("source", source));
        post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        HttpResponse response = httpclient.execute(post);
        int code = response.getStatusLine().getStatusCode();

        if (code == HttpStatus.SC_OK) {
            HttpEntity resEntity = response.getEntity();
            String respStr = EntityUtils.toString(resEntity);
            return respStr
                    .substring(respStr.lastIndexOf(GOOGLE_AUTH_TOKEN_PREFIX) + GOOGLE_AUTH_TOKEN_PREFIX.length())
                    .replaceAll("[\n\r]", "");
        } else {
            EntityUtils.toString(response.getEntity());
            return null;
        }
    }

    /**
     * Refreshes the Google Auth Token.
     * 
     * @return <tt>true</tt> on success, otherwise <tt>false</tt>.
     */
    public boolean refreshAuthToken() {
        /*
         * If the authToken was manually submitted, there is no Google login
         * information available.
         */
        if (email == null) {
            return false;
        }

        try {
            authToken = fetchGoogleAuthToken();
        } catch (Exception e) {
            e.printStackTrace();
            authToken = null;
        }

        return authToken != null ? true : false;
    }

    /**
     * Returns the Google Auth Token.
     * 
     * @return The Google Auth Token.
     */
    public String getAuthToken() {
        return authToken;
    }

    /**
     * Internal method to add the GCP header to each POST HTTP request.
     * 
     * @param post
     *            POST method
     * @throws ClientProtocolException
     * @throws IOException
     */
    private void addGcpHeaders(HttpPost post) throws ClientProtocolException, IOException {
        post.addHeader("Authorization", "GoogleLogin auth=" + authToken);
        post.addHeader("X-CloudPrint-Proxy", source);

        currentPost = post;
    }

    /**
     * Internal method to add the GCP header to each GET HTTP request.
     * 
     * @param post
     *            GET method
     * @throws ClientProtocolException
     * @throws IOException
     */
    private void addGcpHeaders(HttpGet get) throws ClientProtocolException, IOException {
        get.addHeader("Authorization", "GoogleLogin auth=" + authToken);
        get.addHeader("X-CloudPrint-Proxy", source);

        currentGet = get;
    }

    /**
     * Internal method to add the GCP header to each HEAD HTTP request.
     * 
     * @param head
     *            HEAD method
     * @throws ClientProtocolException
     * @throws IOException
     */
    @SuppressWarnings("unused")
    private void addGcpHeaders(HttpHead head) throws ClientProtocolException, IOException {
        head.addHeader("Authorization", "GoogleLogin auth=" + authToken);
        head.addHeader("X-CloudPrint-Proxy", source);

        currentHead = head;
    }

    /**
     * Cancels the current request.
     * 
     * @return <tt>true</tt> on success, otherwise <tt>false</tt>.
     */
    public boolean cancelCurrentRequest() {
        if (currentGet != null) {
            currentGet.abort();
            return currentGet.isAborted();
        }

        if (currentPost != null) {
            currentPost.abort();
            return currentPost.isAborted();
        }

        if (currentHead != null) {
            currentHead.abort();
            return currentHead.isAborted();
        }

        return false;
    }

    /**
     * Registers a printer with the GCP service.
     * 
     * @param printer
     *            Printer to register.
     * @param ppd
     *            PPD file content (optional). May be <tt>null</tt>.
     * @return <tt>true</tt> on success, otherwise <tt>false</tt>.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public boolean register(Printer printer, String ppd)
            throws ClientProtocolException, IOException, JSONException {
        return registerOrUpdate(printer, ppd, GCP_CMD_REGISTER);
    }

    /**
     * Updates the informtion of a GCP printer.
     * 
     * @param printer
     *            Printer to register.
     * @param ppd
     *            PPD file content (optional). May be <tt>null</tt>.
     * @return <tt>true</tt> on success, otherwise <tt>false</tt>.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public boolean update(Printer printer, String ppd) throws ClientProtocolException, IOException, JSONException {
        return registerOrUpdate(printer, ppd, GCP_CMD_UPDATE);
    }

    /**
     * Registers or updates a printer with the GCP service.
     * 
     * @param printer
     *            Printer to register.
     * @param ppd
     *            PPD file content (optional). May be <tt>null</tt>.
     * @param gcpCommand
     *            Command to use, can be <tt>register</tt> of <tt>update</tt>.
     * @return <tt>true</tt> on success, otherwise <tt>false</tt>.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    private boolean registerOrUpdate(Printer printer, String ppd, String gcpCommand)
            throws ClientProtocolException, IOException, JSONException {
        HttpPost post = new HttpPost(GCP_BASE_URL + "/" + gcpCommand);
        addGcpHeaders(post);

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        if (gcpCommand.equals(GCP_CMD_UPDATE)) {
            nvps.add(new BasicNameValuePair("printerid", printer.getId()));
        }
        nvps.add(new BasicNameValuePair("printer", printer.getName()));
        nvps.add(new BasicNameValuePair("proxy", printer.getProxy()));
        nvps.add(new BasicNameValuePair("capabilities", ppd == null || ppd.length() == 0 ? PPD_MINIMAL : ppd));
        nvps.add(new BasicNameValuePair("defaults", ppd));
        nvps.add(new BasicNameValuePair("status", printer.getStatus()));
        nvps.add(new BasicNameValuePair("description", printer.getDescription()));
        nvps.add(new BasicNameValuePair("output", "json"));

        post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        HttpResponse response = httpclient.execute(post);
        int code = response.getStatusLine().getStatusCode();

        String responseStr = EntityUtils.toString(response.getEntity());

        if (code == HttpStatus.SC_OK) {
            JSONObject json = new JSONObject(responseStr);
            if (json.has("message")) {
                lastResponse.setMessage(json.getString("message"));
            }

            if (json.getBoolean("success")) {
                if (gcpCommand.equals(GCP_CMD_REGISTER)) {
                    printer.setId(json.getJSONArray("printers").getJSONObject(0).getString("id"));
                }
                lastResponse.setSuccess(true);
                return true;
            } else {
                lastResponse.setSuccess(false);
                return false;
            }
        } else {
            lastResponse.setSuccess(false);
            lastResponse.setMessage("HTTP result code: " + code);
            return false;
        }
    }

    /**
     * Deletes the specified printer (printer ID has to be available).
     * 
     * @param printer
     *            Printer to delete.
     * @return <tt>true</tt> on success, otherwise <tt>false</tt>.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public boolean delete(Printer printer) throws ClientProtocolException, IOException, JSONException {
        HttpPost post = new HttpPost(GCP_BASE_URL + "/" + GCP_CMD_DELETE);
        addGcpHeaders(post);

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        nvps.add(new BasicNameValuePair("printerid", printer.getId()));
        nvps.add(new BasicNameValuePair("output", "json"));

        post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        HttpResponse response = httpclient.execute(post);
        int code = response.getStatusLine().getStatusCode();

        String responseStr = EntityUtils.toString(response.getEntity());

        if (code == HttpStatus.SC_OK) {
            JSONObject json = new JSONObject(responseStr);
            if (json.has("message")) {
                lastResponse.setMessage(json.getString("message"));
            }

            if (json.getBoolean("success")) {
                lastResponse.setSuccess(true);
                return true;
            } else {
                lastResponse.setSuccess(false);
                return true;
            }
        } else {
            lastResponse.setSuccess(false);
            lastResponse.setMessage("HTTP result code: " + code);
            return false;
        }
    }

    /**
     * Lists all printers for the given print proxy.
     * 
     * @param proxy
     *            Proxy to query.
     * @return List of printers.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public List<Printer> list(String proxy) throws ClientProtocolException, IOException, JSONException {
        HttpPost post = new HttpPost(GCP_BASE_URL + "/" + GCP_CMD_LIST);
        addGcpHeaders(post);

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        nvps.add(new BasicNameValuePair("proxy", proxy));
        nvps.add(new BasicNameValuePair("output", "json"));

        post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        HttpResponse response = httpclient.execute(post);
        int code = response.getStatusLine().getStatusCode();

        String responseStr = EntityUtils.toString(response.getEntity());

        if (code == HttpStatus.SC_OK) {
            JSONObject json = new JSONObject(responseStr);
            if (json.has("message")) {
                lastResponse.setMessage(json.getString("message"));
            }

            if (json.getBoolean("success")) {
                List<Printer> list = new ArrayList<Printer>();

                for (int i = 0; i < json.getJSONArray("printers").length(); i++) {
                    JSONObject obj = json.getJSONArray("printers").getJSONObject(i);
                    Printer printer = new Printer();
                    printer.setId(obj.getString("id"));
                    printer.setName(obj.getString("name"));
                    printer.setDescription(obj.getString("description"));
                    printer.setProxy(obj.getString("proxy"));
                    list.add(printer);
                }

                lastResponse.setSuccess(true);
                return list;
            } else {
                lastResponse.setSuccess(false);
                return null;
            }
        } else {
            lastResponse.setSuccess(false);
            lastResponse.setMessage("HTTP result code: " + code);
            return null;
        }
    }

    /**
     * Searches a printer by query string. A detailed description of the query
     * string can be found at the Google GCP page.<br />
     * 
     * @see <a
     *      href="http://code.google.com/intl/de-DE/apis/cloudprint/docs/appInterfaces.html#search">Google
     *      Cloud Print - Service Interfaces</a>
     * 
     * @param q
     * @return List of printers found.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public List<Printer> search(String q) throws ClientProtocolException, IOException, JSONException {
        HttpPost post = new HttpPost(GCP_BASE_URL + "/" + GCP_CMD_SEARCH);
        addGcpHeaders(post);

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();

        if (q != null) {
            nvps.add(new BasicNameValuePair("q", q));
            nvps.add(new BasicNameValuePair("output", "json"));
        }

        post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        HttpResponse response = httpclient.execute(post);
        int code = response.getStatusLine().getStatusCode();

        String responseStr = EntityUtils.toString(response.getEntity());

        if (code == HttpStatus.SC_OK) {
            JSONObject json = new JSONObject(responseStr);
            if (json.has("message")) {
                lastResponse.setMessage(json.getString("message"));
            }

            if (json.getBoolean("success")) {
                List<Printer> list = new ArrayList<Printer>();

                for (int i = 0; i < json.getJSONArray("printers").length(); i++) {
                    JSONObject obj = json.getJSONArray("printers").getJSONObject(i);
                    Printer printer = new Printer();
                    printer.setId(obj.getString("id"));
                    printer.setName(obj.getString("name"));
                    printer.setProxy(obj.getString("proxy"));
                    list.add(printer);
                }

                lastResponse.setSuccess(true);
                return list;
            } else {
                lastResponse.setSuccess(false);
                return null;
            }
        } else {
            lastResponse.setSuccess(false);
            lastResponse.setMessage("HTTP result code: " + code);
            return null;
        }
    }

    /**
     * Fetches the available print job for the specified printer. Only print
     * jobs with state <tt>QUEUED</tt> will be returned.
     * 
     * @param printer
     *            Printer to fetch jobs from.
     * @return List of available print jobs.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public List<Job> fetch(Printer printer) throws ClientProtocolException, IOException, JSONException {
        HttpPost post = new HttpPost(GCP_BASE_URL + "/" + GCP_CMD_FETCH);
        addGcpHeaders(post);

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        nvps.add(new BasicNameValuePair("printerid", printer.getId()));
        nvps.add(new BasicNameValuePair("output", "json"));

        post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        HttpResponse response = httpclient.execute(post);
        int code = response.getStatusLine().getStatusCode();

        String responseStr = EntityUtils.toString(response.getEntity());

        if (code == HttpStatus.SC_OK) {
            JSONObject json = new JSONObject(responseStr);
            if (json.has("message")) {
                lastResponse.setMessage(json.getString("message"));
            }

            if (json.getBoolean("success")) {
                List<Job> list = new ArrayList<Job>();

                for (int i = 0; i < json.getJSONArray("jobs").length(); i++) {
                    JSONObject obj = json.getJSONArray("jobs").getJSONObject(i);
                    Job job = new Job();
                    job.setId(obj.getString("id"));
                    job.setTitle(obj.getString("title"));
                    job.setStatus(obj.getString("status"));
                    job.setFileUrl(obj.getString("fileUrl"));
                    job.setTicketUrl(obj.getString("ticketUrl"));
                    job.setContentType(obj.getString("contentType"));
                    list.add(job);
                }

                lastResponse.setSuccess(true);
                return list;
            } else {
                lastResponse.setSuccess(false);
                return null;
            }
        } else {
            lastResponse.setSuccess(false);
            lastResponse.setMessage("HTTP result code: " + code);
            return null;
        }
    }

    /**
     * Returns a list of all print jobs (independent of state) for all or the
     * specified printer.
     * 
     * @param printer
     *            Printer to query (optional).
     * @return List of availabel print jobs.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public List<Job> jobs(Printer printer) throws ClientProtocolException, IOException, JSONException {
        HttpPost post = new HttpPost(GCP_BASE_URL + "/" + GCP_CMD_JOBS);
        addGcpHeaders(post);

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        if (printer != null) {
            nvps.add(new BasicNameValuePair("printerid", printer.getId()));
        }
        nvps.add(new BasicNameValuePair("output", "json"));

        post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        HttpResponse response = httpclient.execute(post);
        int code = response.getStatusLine().getStatusCode();

        String responseStr = EntityUtils.toString(response.getEntity());
        System.out.println(responseStr);
        if (code == HttpStatus.SC_OK) {
            JSONObject json = new JSONObject(responseStr);
            if (json.has("message")) {
                lastResponse.setMessage(json.getString("message"));
            }

            if (json.getBoolean("success")) {
                List<Job> list = new ArrayList<Job>();

                for (int i = 0; i < json.getJSONArray("jobs").length(); i++) {
                    JSONObject obj = json.getJSONArray("jobs").getJSONObject(i);
                    Job job = new Job();
                    job.setId(obj.getString("id"));
                    job.setPrinterId(obj.getString("printerid"));
                    job.setTitle(obj.getString("title"));
                    job.setContentType(obj.getString("contentType"));
                    job.setFileUrl(obj.getString("fileUrl"));
                    job.setTicketUrl(obj.getString("ticketUrl"));
                    job.setStatus(obj.getString("status"));
                    job.setErrorCode(obj.getString("errorCode"));
                    job.setMessage(obj.getString("message"));

                    List<String> tags = new ArrayList<String>();
                    for (int j = 0; j < obj.getJSONArray("tags").length(); j++) {
                        tags.add(obj.getJSONArray("tags").getString(j));
                    }
                    job.setTags(tags);

                    job.setCreateTime(new Date(Long.parseLong(obj.getString("createTime"))));
                    job.setUpdateTime(new Date(Long.parseLong(obj.getString("updateTime"))));
                    list.add(job);
                }

                lastResponse.setSuccess(true);
                return list;
            } else {
                lastResponse.setSuccess(false);
                return null;
            }
        } else {
            lastResponse.setSuccess(false);
            lastResponse.setMessage("HTTP result code: " + code);
            return null;
        }
    }

    /**
     * Retrieves information about the specified printer.
     * 
     * @param printer
     *            Printer to query.
     * @return <tt>true</tt> on success, otherwise <tt>false</tt>.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public boolean printer(Printer printer) throws ClientProtocolException, IOException, JSONException {
        HttpPost post = new HttpPost(GCP_BASE_URL + "/" + GCP_CMD_PRINTER);
        addGcpHeaders(post);

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        nvps.add(new BasicNameValuePair("printerid", printer.getId()));
        nvps.add(new BasicNameValuePair("output", "json"));

        post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        HttpResponse response = httpclient.execute(post);
        int code = response.getStatusLine().getStatusCode();

        String responseStr = EntityUtils.toString(response.getEntity());

        if (code == HttpStatus.SC_OK) {
            JSONObject json = new JSONObject(responseStr);
            if (json.has("message")) {
                lastResponse.setMessage(json.getString("message"));
            }

            if (json.getBoolean("success")) {
                for (int i = 0; i < json.getJSONArray("printers").length(); i++) {
                    JSONObject obj = json.getJSONArray("printers").getJSONObject(i);
                    printer.setId(obj.getString("id"));
                    printer.setName(obj.getString("name"));
                    printer.setProxy(obj.getString("proxy"));

                    /* 
                     * No longer available in final specification!
                     */
                    /*
                    printer.setNumberOfDocuments(Integer.parseInt(obj
                          .getString("numberOfDocuments")));
                              
                    printer.setNumberOfPages(Integer.parseInt(obj
                          .getString("numberOfPages")));
                    */

                    printer.setCapsFormat(obj.getString("capsFormat"));

                    printer.setCreateTime(new Date(Long.parseLong(obj.getString("createTime"))));
                    printer.setUpdateTime(new Date(Long.parseLong(obj.getString("updateTime"))));
                    printer.setCreateTime(new Date(Long.parseLong(obj.getString("createTime"))));
                    printer.setAccessTime(new Date(Long.parseLong(obj.getString("accessTime"))));

                    List<String> tags = new ArrayList<String>();
                    for (int j = 0; j < obj.getJSONArray("tags").length(); j++) {
                        tags.add(obj.getJSONArray("tags").getString(j));
                    }
                    printer.setTags(tags);

                    List<AccessRole> roles = new ArrayList<AccessRole>();
                    for (int j = 0; j < obj.getJSONArray("access").length(); j++) {
                        JSONObject access = obj.getJSONArray("access").getJSONObject(j);
                        AccessRole role = new AccessRole();
                        role.setEmail(access.getString("email"));
                        role.setName(access.getString("name"));
                        role.setRole(access.getString("role"));
                        roles.add(role);
                    }
                    printer.setAccess(roles);

                    printer.setCapabilities(obj.getJSONArray("capabilities").toString());
                }

                lastResponse.setSuccess(true);
                return true;
            } else {
                lastResponse.setSuccess(false);
                return false;
            }
        } else {
            lastResponse.setSuccess(false);
            lastResponse.setMessage("HTTP result code: " + code);
            return false;
        }
    }

    /**
     * Deltes a print job.
     * 
     * @param job
     *            Print job to delete.
     * @return <tt>true</tt> on success, otherwise <tt>false</tt>.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public boolean deletJob(Job job) throws ClientProtocolException, IOException, JSONException {
        HttpPost post = new HttpPost(GCP_BASE_URL + "/" + GCP_CMD_DELETE_JOB);
        addGcpHeaders(post);

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        nvps.add(new BasicNameValuePair("jobid", job.getId()));
        nvps.add(new BasicNameValuePair("output", "json"));

        post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        HttpResponse response = httpclient.execute(post);
        int code = response.getStatusLine().getStatusCode();

        String responseStr = EntityUtils.toString(response.getEntity());

        if (code == HttpStatus.SC_OK) {
            JSONObject json = new JSONObject(responseStr);
            if (json.has("message")) {
                lastResponse.setMessage(json.getString("message"));
            }

            if (json.getBoolean("success")) {
                lastResponse.setSuccess(true);
                return true;
            } else {
                lastResponse.setSuccess(false);
                return false;
            }
        } else {
            lastResponse.setSuccess(false);
            lastResponse.setMessage("HTTP result code: " + code);
            return false;
        }
    }

    /**
     * Sets the state of print job.
     * 
     * @param job
     *            Job to set state.
     * @param status
     *            Status
     * @param errorCode
     *            Error Code (optional)
     * @param errorMessage
     *            Error Messge (optional). Has to be provided in case of error
     *            code.
     * @return <tt>true</tt> on success, otherwise <tt>false</tt>.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public boolean control(Job job, String status, String errorCode, String errorMessage)
            throws ClientProtocolException, IOException, JSONException {
        HttpPost post = new HttpPost(GCP_BASE_URL + "/" + GCP_CMD_CONTROL);
        addGcpHeaders(post);

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        nvps.add(new BasicNameValuePair("jobid", job.getId()));
        nvps.add(new BasicNameValuePair("status", status));
        if (status.equals(JOB_STATUS_ERROR)) {
            nvps.add(new BasicNameValuePair("code", errorCode));
            nvps.add(new BasicNameValuePair("message", errorMessage));
        }

        nvps.add(new BasicNameValuePair("output", "json"));

        post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        HttpResponse response = httpclient.execute(post);
        int code = response.getStatusLine().getStatusCode();

        String responseStr = EntityUtils.toString(response.getEntity());

        if (code == HttpStatus.SC_OK) {
            JSONObject json = new JSONObject(responseStr);
            if (json.has("message")) {
                lastResponse.setMessage(json.getString("message"));
            }

            if (json.getBoolean("success")) {
                lastResponse.setSuccess(true);
                return true;
            } else {
                lastResponse.setSuccess(false);
                return false;
            }
        } else {
            lastResponse.setSuccess(false);
            lastResponse.setMessage("HTTP result code: " + code);
            return false;
        }
    }

    /**
     * Returns an Input Stream of job to download.
     * 
     * @param job
     *            Job to download.
     * @param mimetype
     *            Mime Type
     * @return Input Stream that can be used to download the job.
     * @throws ClientProtocolException
     * @throws IOException
     */
    public InputStream downloadJob(Job job, String mimetype) throws ClientProtocolException, IOException {
        HttpGet get = new HttpGet(job.getFileUrl());
        addGcpHeaders(get);
        get.addHeader("Accept", mimetype);

        HttpResponse response = httpclient.execute(get);
        int code = response.getStatusLine().getStatusCode();

        if (code == HttpStatus.SC_OK) {
            return response.getEntity().getContent();
        } else {
            return null;
        }

    }

    /**
     * Submits a new print job.
     * 
     * @param printer
     *            Printer to use.
     * @param document
     *            Document to send.
     * @param contentType
     *            Mime Type
     * @param capabilities
     *            Capabilities (optional).
     * @return <tt>true</tt> on success, otherwise <tt>false</tt>.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public boolean submit(Printer printer, File document, String contentType, String capabilities)
            throws ClientProtocolException, IOException, JSONException {
        return submit(printer, document, contentType, capabilities, null);
    }

    /**
     * Submits a new print job.
     * 
     * @param printer
     *            Printer to use.
     * @param document
     *            Document to send.
     * @param contentType
     *            Mime Type
     * @param capabilities
     *            Capabilities (optional).
     * @param listener
     *            Update listener that receives upload notifications in percent.
     * @return <tt>true</tt> on success, otherwise <tt>false</tt>.
     * @throws ClientProtocolException
     * @throws IOException
     * @throws JSONException
     */
    public boolean submit(Printer printer, File document, String contentType, String capabilities,
            UploadListener listener) throws ClientProtocolException, IOException, JSONException {
        if (!document.exists()) {
            lastResponse.setSuccess(false);
            lastResponse.setMessage("Document does not exist.");
            return false;
        }

        if (capabilities == null) {
            capabilities = "";
        }

        HttpPost post = new HttpPost(GCP_BASE_URL + "/" + GCP_CMD_SUBMIT);
        addGcpHeaders(post);

        FileBody doc = new CountingFileBody(document, listener);

        MultipartEntity reqEntity = new MultipartEntity();
        reqEntity.addPart("content", doc);
        reqEntity.addPart("output", new StringBody("json", Charset.forName("UTF-8")));
        reqEntity.addPart("printerid", new StringBody(printer.getId(), Charset.forName("UTF-8")));
        reqEntity.addPart("capabilities", new StringBody(capabilities, Charset.forName("UTF-8")));
        reqEntity.addPart("contentType", new StringBody(contentType, Charset.forName("UTF-8")));
        reqEntity.addPart("title", new StringBody(document.getName(), Charset.forName("UTF-8")));
        post.setEntity(reqEntity);

        HttpResponse response = httpclient.execute(post);
        int code = response.getStatusLine().getStatusCode();

        String responseStr = EntityUtils.toString(response.getEntity());

        if (code == HttpStatus.SC_OK) {
            JSONObject json = new JSONObject(responseStr);
            if (json.has("message")) {
                lastResponse.setMessage(json.getString("message"));
            }

            if (json.getBoolean("success")) {
                lastResponse.setSuccess(true);
                return true;
            } else {
                lastResponse.setSuccess(false);
                return false;
            }
        } else {
            lastResponse.setSuccess(false);
            lastResponse.setMessage("HTTP result code: " + code);
            return false;
        }
    }
}