| /* IppRequest.java -- |
| Copyright (C) 2006 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package gnu.javax.print.ipp; |
| |
| import gnu.classpath.debug.Component; |
| import gnu.classpath.debug.SystemLogger; |
| import gnu.javax.print.ipp.attribute.CharsetSyntax; |
| import gnu.javax.print.ipp.attribute.NaturalLanguageSyntax; |
| import gnu.javax.print.ipp.attribute.RequestedAttributes; |
| import gnu.javax.print.ipp.attribute.job.AttributesCharset; |
| import gnu.javax.print.ipp.attribute.job.AttributesNaturalLanguage; |
| import gnu.javax.print.ipp.attribute.job.JobId; |
| import gnu.javax.print.ipp.attribute.job.JobUri; |
| import gnu.javax.print.ipp.attribute.printer.DocumentFormat; |
| |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.HttpURLConnection; |
| import java.net.URI; |
| import java.net.URL; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.GregorianCalendar; |
| import java.util.List; |
| import java.util.logging.Logger; |
| |
| import javax.print.attribute.Attribute; |
| import javax.print.attribute.AttributeSet; |
| import javax.print.attribute.DateTimeSyntax; |
| import javax.print.attribute.EnumSyntax; |
| import javax.print.attribute.HashAttributeSet; |
| import javax.print.attribute.IntegerSyntax; |
| import javax.print.attribute.ResolutionSyntax; |
| import javax.print.attribute.SetOfIntegerSyntax; |
| import javax.print.attribute.TextSyntax; |
| import javax.print.attribute.URISyntax; |
| import javax.print.attribute.standard.Compression; |
| import javax.print.attribute.standard.Copies; |
| import javax.print.attribute.standard.DocumentName; |
| import javax.print.attribute.standard.Fidelity; |
| import javax.print.attribute.standard.Finishings; |
| import javax.print.attribute.standard.JobHoldUntil; |
| import javax.print.attribute.standard.JobImpressions; |
| import javax.print.attribute.standard.JobKOctets; |
| import javax.print.attribute.standard.JobMediaSheets; |
| import javax.print.attribute.standard.JobName; |
| import javax.print.attribute.standard.JobOriginatingUserName; |
| import javax.print.attribute.standard.JobPriority; |
| import javax.print.attribute.standard.JobSheets; |
| import javax.print.attribute.standard.Media; |
| import javax.print.attribute.standard.MultipleDocumentHandling; |
| import javax.print.attribute.standard.NumberUp; |
| import javax.print.attribute.standard.OrientationRequested; |
| import javax.print.attribute.standard.PageRanges; |
| import javax.print.attribute.standard.PrintQuality; |
| import javax.print.attribute.standard.PrinterResolution; |
| import javax.print.attribute.standard.PrinterURI; |
| import javax.print.attribute.standard.RequestingUserName; |
| import javax.print.attribute.standard.SheetCollate; |
| import javax.print.attribute.standard.Sides; |
| |
| /** |
| * <code>IppRequest</code> models a request to an IPP compatible |
| * server as described in RFC 2910 - IPP/1.1: Encoding and Transport. |
| * <p> |
| * The byte stream is structured as follows (for an official description |
| * please have a look at the RFC document mentioned above): |
| * <ul> |
| * <li>version-number - 2 bytes - required</li> |
| * <li>operation-id - 2 bytes - required</li> |
| * <li>request-id - 4 bytes - required</li> |
| * <li>attribute-group - n bytes - 0 or more</li> |
| * <li>end-of-attributes-tag - 1 byte - required</li> |
| * <li>data - q bytes - optional</li> |
| * </ul> |
| * </p> |
| * |
| * @author Wolfgang Baer (WBaer@gmx.de) |
| */ |
| public class IppRequest |
| { |
| |
| /** |
| * The printer-poll timeout. |
| */ |
| private static final int timeout = 1000; |
| |
| /** |
| * Helper class used to write the attributes of a request |
| * into the supplied data output stream in the correct way. |
| * |
| * @author Wolfgang Baer (WBaer@gmx.de) |
| */ |
| class RequestWriter |
| { |
| private DataOutputStream out; |
| |
| /** |
| * Creates a RequestWriter. |
| * |
| * @param stream the stream to write to. |
| */ |
| RequestWriter(DataOutputStream stream) |
| { |
| out = stream; |
| } |
| |
| /** |
| * Writes an attribute in IntegerSyntax into the stream. |
| * @param attribute the attribute |
| * @throws IOException if thrown by the stream |
| */ |
| private void write(IntegerSyntax attribute) throws IOException |
| { |
| String name = ((Attribute) attribute).getName(); |
| out.writeByte(IppValueTag.INTEGER); |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| out.writeShort(4); // length, integer is 4 bytes |
| out.writeInt(attribute.getValue()); |
| } |
| |
| /** |
| * Writes an attribute in EnumSyntax into the stream. |
| * @param attribute the attribute |
| * @throws IOException if thrown by the stream |
| */ |
| private void write(EnumSyntax attribute) throws IOException |
| { |
| // in JPS API enum syntax is used for enums, keyword and boolean types |
| String name = ((Attribute) attribute).getName(); |
| |
| // the enum value types |
| if (attribute instanceof Finishings |
| || attribute instanceof OrientationRequested |
| || attribute instanceof PrintQuality) |
| { |
| out.writeByte(IppValueTag.ENUM); |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| out.writeShort(4); // length, enum is 4 bytes |
| out.writeInt(attribute.getValue()); |
| } |
| // the boolean value type |
| else if (attribute instanceof Fidelity) |
| { |
| out.writeByte(IppValueTag.BOOLEAN); |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| out.writeShort(1); // length, boolean is 1 bytes |
| out.writeByte(attribute.getValue() == 0 ? 0x00 : 0x01); |
| } |
| // the keyword value types |
| else |
| { |
| String keyword = attribute.toString(); |
| out.writeByte(IppValueTag.KEYWORD); |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| out.writeShort(keyword.length()); |
| out.write(keyword.getBytes()); |
| } |
| } |
| |
| /** |
| * Writes an attribute in SetOfIntegerSyntax into the stream. |
| * @param attribute the attribute |
| * @throws IOException if thrown by the stream |
| */ |
| private void write(SetOfIntegerSyntax attribute) throws IOException |
| { |
| String name = ((Attribute) attribute).getName(); |
| int[][] ranges = attribute.getMembers(); |
| for (int i = 0; i < ranges.length; i++) |
| { |
| out.writeByte(IppValueTag.RANGEOFINTEGER); |
| if (i == 0) |
| { |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| } |
| else |
| out.writeShort(0x0000); // only name-length |
| |
| out.writeShort(8); // range is 8 bytes |
| out.writeInt(ranges[i][0]); |
| out.writeInt(ranges[i][1]); |
| } |
| } |
| |
| /** |
| * Writes an attribute in ResolutionSyntax into the stream. |
| * @param attribute the attribute |
| * @throws IOException if thrown by the stream |
| */ |
| private void write(ResolutionSyntax attribute) throws IOException |
| { |
| String name = ((Attribute) attribute).getName(); |
| out.writeByte(IppValueTag.RESOLUTION); |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| out.writeShort(9); // length fixed to 9 |
| out.writeInt(attribute.getCrossFeedResolution(ResolutionSyntax.DPI)); |
| out.writeInt(attribute.getFeedResolution(ResolutionSyntax.DPI)); |
| out.writeByte(ResolutionSyntax.DPI); |
| } |
| |
| /** |
| * Writes an attribute in DateTimeSyntax into the stream. |
| * <p> |
| * The syntax value is defined as 11 octets follwing the |
| * DateAndTime format of RFC 1903. (see IppResponse) |
| * </p> |
| * |
| * @param attribute the attribute |
| * @throws IOException if thrown by the stream |
| */ |
| private void write(DateTimeSyntax attribute) throws IOException |
| { |
| String name = ((Attribute) attribute).getName(); |
| out.writeByte(IppValueTag.DATETIME); |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| out.writeShort(11); // length fixed to 11 |
| |
| Date date = attribute.getValue(); |
| Calendar cal = new GregorianCalendar(); |
| cal.setTime(date); |
| |
| out.writeShort(cal.get(Calendar.YEAR)); |
| out.writeByte(cal.get(Calendar.MONTH)); |
| out.writeByte(cal.get(Calendar.DAY_OF_MONTH)); |
| out.writeByte(cal.get(Calendar.HOUR_OF_DAY)); |
| out.writeByte(cal.get(Calendar.MINUTE)); |
| int second = cal.get(Calendar.SECOND); |
| out.writeByte(second == 0 ? 60 : second); |
| out.writeByte(cal.get(Calendar.MILLISECOND) / 100); |
| |
| int offsetInMillis = cal.get(Calendar.ZONE_OFFSET); |
| char directionFromUTC = '+'; |
| if (offsetInMillis < 0) |
| { |
| directionFromUTC = '-'; |
| offsetInMillis = offsetInMillis * (-1); |
| } |
| |
| out.writeByte(directionFromUTC); |
| out.writeByte(offsetInMillis / 3600000); // hours |
| out.writeByte((offsetInMillis % 3600000) / 60000); // minutes |
| } |
| |
| /** |
| * Writes an attribute in TextSyntax into the stream. |
| * <p> |
| * By default attributes are qritten as TEXT_WITHOUT_LANGUAGE value-tag. |
| * As some attributes in the JPS are TextSyntax attributes but actually |
| * of NAME value-tag in IPP this method checks for these attributes and |
| * writes them as NAME_WITHOUT_LANGUAGE value-tag into the stream. |
| * </p> |
| * |
| * @param attribute the attribute |
| * @param out the stream to write to |
| * @throws IOException if thrown by the stream |
| */ |
| private void write(TextSyntax attribute) throws IOException |
| { |
| // We only use *WithoutLanguage, correct according to spec. |
| String name = ((Attribute) attribute).getName(); |
| |
| if (attribute instanceof RequestingUserName |
| || attribute instanceof JobName |
| || attribute instanceof DocumentName |
| || attribute instanceof JobOriginatingUserName) |
| out.writeByte(IppValueTag.NAME_WITHOUT_LANGUAGE); |
| else if (attribute instanceof DocumentFormat) |
| out.writeByte(IppValueTag.MIME_MEDIA_TYPE); |
| else |
| out.writeByte(IppValueTag.TEXT_WITHOUT_LANGUAGE); |
| |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| out.writeShort(attribute.getValue().length()); |
| out.write(attribute.getValue().getBytes()); |
| } |
| |
| /** |
| * Writes an attribute in URISyntax into the stream. |
| * @param attribute the attribute |
| * @param out the stream to write to |
| * @throws IOException if thrown by the stream |
| */ |
| private void write(URISyntax attribute) throws IOException |
| { |
| // only uriScheme syntax type should not appear |
| // in a request (reference-uri-schemes-supported) |
| String name = ((Attribute) attribute).getName(); |
| String uriAscii = attribute.getURI().toASCIIString(); |
| out.writeByte(IppValueTag.URI); |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| out.writeShort(uriAscii.length()); |
| out.write(uriAscii.getBytes()); |
| } |
| |
| /** |
| * Writes an attribute in CharsetSyntax into the stream. |
| * @param attribute the attribute |
| * @param out the stream to write to |
| * @throws IOException if thrown by the stream |
| */ |
| private void write(CharsetSyntax attribute) throws IOException |
| { |
| String name = ((Attribute) attribute).getName(); |
| out.writeByte(IppValueTag.CHARSET); |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| out.writeShort(attribute.getValue().length()); |
| out.write(attribute.getValue().getBytes()); |
| } |
| |
| /** |
| * Writes an attribute in NaturalLanguageSyntax into the stream. |
| * @param attribute the attribute |
| * @param out the stream to write to |
| * @throws IOException if thrown by the stream |
| */ |
| private void write(NaturalLanguageSyntax attribute) throws IOException |
| { |
| String name = ((Attribute) attribute).getName(); |
| out.writeByte(IppValueTag.NATURAL_LANGUAGE); |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| out.writeShort(attribute.getValue().length()); |
| out.write(attribute.getValue().getBytes()); |
| } |
| |
| /** |
| * Writes an attribute in RequestedAttributes into the stream. |
| * @param attribute the attribute |
| * @param out the stream to write to |
| * @throws IOException if thrown by the stream |
| */ |
| private void write(RequestedAttributes attribute) throws IOException |
| { |
| List values = attribute.getValues(); |
| |
| String name = ((Attribute) attribute).getName(); |
| out.writeByte(IppValueTag.KEYWORD); |
| out.writeShort(name.length()); |
| out.write(name.getBytes()); |
| out.writeShort(((String) values.get(0)).length()); |
| out.write(((String) values.get(0)).getBytes()); |
| |
| for (int i=1; i < values.size(); i++) |
| { |
| out.writeByte(IppValueTag.KEYWORD); |
| out.writeShort(0x0000); // length for additional value |
| out.writeShort(((String) values.get(i)).length()); |
| out.write(((String) values.get(i)).getBytes()); |
| } |
| } |
| |
| |
| /** |
| * Writes the given operation attribute group of the given map instance |
| * (key=group, values=set of attributes) into the supplied data |
| * output stream. |
| * |
| * @param attributes the set with the attributes. |
| * |
| * @throws IOException if thrown by the used DataOutputStream. |
| * @throws IppException if unknown attributes occur. |
| */ |
| public void writeOperationAttributes(AttributeSet attributes) |
| throws IOException, IppException |
| { |
| out.write(IppDelimiterTag.OPERATION_ATTRIBUTES_TAG); |
| |
| // its essential to write these two in this order and as first ones |
| Attribute att = attributes.get(AttributesCharset.class); |
| write((CharsetSyntax) att); |
| |
| logger.log(Component.IPP, "Attribute: Name: <" |
| + att.getCategory().getName() + "> Value: <" + att.toString() + ">"); |
| |
| attributes.remove(AttributesCharset.class); |
| |
| att = attributes.get(AttributesNaturalLanguage.class); |
| write((NaturalLanguageSyntax) att); |
| attributes.remove(AttributesNaturalLanguage.class); |
| |
| logger.log(Component.IPP, "Attribute: Name: <" |
| + att.getCategory().getName() + "> Value: <" + att.toString() + ">"); |
| |
| // furthermore its essential to now write out the target attribute |
| PrinterURI printerUri = (PrinterURI) attributes.get(PrinterURI.class); |
| JobUri jobUri = (JobUri) attributes.get(JobUri.class); |
| JobId jobId = (JobId) attributes.get(JobId.class); |
| if (printerUri != null && jobId == null && jobUri == null) |
| { |
| write(printerUri); |
| attributes.remove(PrinterURI.class); |
| logger.log(Component.IPP, "Attribute: Name: <" + printerUri |
| .getCategory().getName() + "> Value: <" + printerUri.toString() + ">"); |
| } |
| else if (jobUri != null && jobId == null && printerUri == null) |
| { |
| write(jobUri); |
| attributes.remove(JobUri.class); |
| logger.log(Component.IPP, "Attribute: Name: <" + jobUri |
| .getCategory().getName() + "> Value: <" + jobUri.toString() + ">"); |
| } |
| else if (printerUri != null && jobId != null && jobUri == null) |
| { |
| write(printerUri); // must be third |
| write(jobId); |
| attributes.remove(PrinterURI.class); |
| attributes.remove(JobId.class); |
| logger.log(Component.IPP, "Attribute: Name: <" + printerUri |
| .getCategory().getName() + "> Value: <" + printerUri.toString() + ">"); |
| logger.log(Component.IPP, "Attribute: Name: <" + jobId.getCategory() |
| .getName() + "> Value: <" + jobId.toString() + ">"); |
| } |
| else if (jobUri != null && jobId != null) |
| { |
| write(jobUri); |
| attributes.remove(JobUri.class); |
| attributes.remove(JobId.class); // MUST NOT redundant |
| logger.log(Component.IPP, "Attribute: Name: <" + jobUri.getCategory() |
| .getName() + "> Value: <" + jobUri.toString() + ">"); |
| } |
| else |
| { |
| new IppException("Unknown target operation attribute combination."); |
| } |
| |
| writeAttributes(attributes); |
| } |
| |
| /** |
| * Writes the given attribute groups of the given map instance |
| * (key=group, values=set of attributes) into the supplied data |
| * output stream. |
| * |
| * @param attributes the set with the attributes. |
| * |
| * @throws IOException if thrown by the used DataOutputStream. |
| * @throws IppException if unknown attributes occur. |
| */ |
| public void writeAttributes(AttributeSet attributes) |
| throws IOException, IppException |
| { |
| Attribute[] attributeArray = attributes.toArray(); |
| for (int i = 0; i < attributeArray.length; i++) |
| { |
| logger.log(Component.IPP, "Attribute: Name: <" + attributeArray[i] |
| .getCategory().getName() + "> Value: <" |
| + attributeArray[i].toString() + ">"); |
| |
| if (attributeArray[i] instanceof IntegerSyntax) |
| write((IntegerSyntax) attributeArray[i]); |
| else if (attributeArray[i] instanceof TextSyntax) |
| write((TextSyntax) attributeArray[i]); |
| else if (attributeArray[i] instanceof DateTimeSyntax) |
| write((DateTimeSyntax) attributeArray[i]); |
| else if (attributeArray[i] instanceof ResolutionSyntax) |
| write((ResolutionSyntax) attributeArray[i]); |
| else if (attributeArray[i] instanceof SetOfIntegerSyntax) |
| write((SetOfIntegerSyntax) attributeArray[i]); |
| else if (attributeArray[i] instanceof EnumSyntax) |
| write((EnumSyntax) attributeArray[i]); |
| else if (attributeArray[i] instanceof URISyntax) |
| write((URISyntax) attributeArray[i]); |
| else if (attributeArray[i] instanceof CharsetSyntax) |
| write((CharsetSyntax) attributeArray[i]); |
| else if (attributeArray[i] instanceof NaturalLanguageSyntax) |
| write((NaturalLanguageSyntax) attributeArray[i]); |
| else if (attributeArray[i] instanceof RequestedAttributes) |
| write((RequestedAttributes) attributeArray[i]); |
| else |
| throw new IppException("Unknown syntax type"); |
| } |
| } |
| |
| } |
| |
| /** |
| * Logger for tracing - enable by passing |
| * -Dgnu.classpath.debug.components=ipp to the vm. |
| */ |
| static final Logger logger = SystemLogger.SYSTEM; |
| |
| /** |
| * The request id counter simply counts up |
| * to give unique request ids per JVM instance. |
| */ |
| private static int requestIdCounter = 1; |
| |
| /** The IPP version defaults to 1.1 */ |
| private static final short VERSION = 0x0101; |
| |
| /** Signals if the request is already on its way */ |
| private boolean alreadySent = false; |
| |
| /** The operation type of this request. */ |
| private short operation_id; |
| |
| /** |
| * The request id of this request. This is |
| * assigned automatically by the constructor. |
| */ |
| private final int request_id; |
| |
| private AttributeSet operationAttributes; |
| |
| private AttributeSet printerAttributes; |
| |
| private AttributeSet jobAttributes; |
| |
| private Object data; |
| |
| private URI requestUri; |
| |
| /** The underlying connection - IPP is http based */ |
| private HttpURLConnection connection; |
| |
| /** |
| * Creates an IPPRequest instance. |
| * |
| * @param uri the URI of the request |
| * @param user the user if any |
| * @param password the password of the supplied user |
| */ |
| public IppRequest(URI uri, String user, String password) |
| { |
| request_id = incrementRequestIdCounter(); |
| requestUri = uri; |
| |
| try |
| { |
| URL url = new URL("http", |
| user == null |
| ? uri.getHost() : user + ":" |
| + password + "@" + uri.getHost(), |
| uri.getPort(), uri.getPath()); |
| |
| connection = (HttpURLConnection) url.openConnection(); |
| connection.setRequestMethod("POST"); |
| connection.setDoOutput(true); |
| |
| connection.setRequestProperty("Content-type", "application/ipp"); |
| connection.setRequestProperty("Accept", "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"); |
| } |
| catch (IOException e) |
| { |
| // MalformedURLException - uri is already checked |
| // ProtocolException - POST is correct method type |
| // IOException -HTTPURLConnection constructor actually |
| // does never throw this exception. |
| logger.log(Component.IPP, "Unexpected IOException", e); |
| } |
| |
| logger.log(Component.IPP, "[IppConnection] Host: " + uri.getHost() |
| + " Port: " + uri.getPort() + " Path: " |
| + uri.getPath()); |
| } |
| |
| /** |
| * Synchronized method to be called by the constructor |
| * to assign a unique request id to this request. |
| * |
| * @return The unique request id. |
| */ |
| private synchronized int incrementRequestIdCounter() |
| { |
| return IppRequest.requestIdCounter++; |
| } |
| |
| /** |
| * Returns the id of this request. |
| * |
| * @return The request ID. |
| */ |
| public int getRequestID() |
| { |
| return request_id; |
| } |
| |
| /** |
| * Sets the data of the request. The data used in this |
| * request will be the one of the supplied inputstream |
| * instead of the alternative byte array possibility. |
| * |
| * @param stream the input stream to use for the data. |
| */ |
| public void setData(InputStream stream) |
| { |
| data = stream; |
| } |
| |
| /** |
| * Sets the data of the request. The data used in this |
| * request will be the one of the supplied byte[] |
| * instead of the alternative input stream possibility. |
| * |
| * @param bytes the byte[] to use for the data. |
| */ |
| public void setData(byte[] bytes) |
| { |
| data = bytes; |
| } |
| |
| /** |
| * Sets the operation id for this request. |
| * |
| * @param id the operation id. |
| */ |
| public void setOperationID(short id) |
| { |
| operation_id = id; |
| } |
| |
| /** |
| * Adds the default values for the operation |
| * attributes "attributes-charset" and |
| * "attributes-natural-language" |
| */ |
| public void setOperationAttributeDefaults() |
| { |
| if (operationAttributes == null) |
| operationAttributes = new HashAttributeSet(); |
| |
| operationAttributes.add(AttributesCharset.UTF8); |
| operationAttributes.add(AttributesNaturalLanguage.EN); |
| } |
| |
| /** |
| * Add the job attribute of this request to the given |
| * attribute set. |
| * |
| * @param attribute the job attribute. |
| */ |
| public void addJobAttribute(Attribute attribute) |
| { |
| if (jobAttributes == null) |
| jobAttributes = new HashAttributeSet(); |
| |
| jobAttributes.add(attribute); |
| } |
| |
| /** |
| * Sets the printer attribute of this request to the given |
| * attribute set. |
| * |
| * @param attribute the printer attribute. |
| */ |
| public void addPrinterAttributes(Attribute attribute) |
| { |
| if (printerAttributes == null) |
| printerAttributes = new HashAttributeSet(); |
| |
| printerAttributes.add(attribute); |
| } |
| |
| /** |
| * Adds the given attribute to the operation attributes set. |
| * |
| * @param attribute the operation attribute to add. |
| */ |
| public void addOperationAttribute(Attribute attribute) |
| { |
| if (operationAttributes == null) |
| operationAttributes = new HashAttributeSet(); |
| |
| operationAttributes.add(attribute); |
| } |
| |
| /** |
| * Filters from the given attribute set the job operation out |
| * and adds them to the operation attributes set. |
| * |
| * @param set the attributes to filter, may not be <code>null</code>. |
| */ |
| public void addAndFilterJobOperationAttributes(AttributeSet set) |
| { |
| if (operationAttributes == null) |
| operationAttributes = new HashAttributeSet(); |
| |
| // document-natural-language - not defined in JPS attributes |
| // document-format - specified outside, special treatment |
| Attribute[] tmp = set.toArray(); |
| for (int i = 0; i < tmp.length; i++) |
| { |
| if (tmp[i].getCategory().equals(JobName.class) |
| || tmp[i].getCategory().equals(Fidelity.class) |
| || tmp[i].getCategory().equals(JobImpressions.class) |
| || tmp[i].getCategory().equals(JobKOctets.class) |
| || tmp[i].getCategory().equals(JobMediaSheets.class) |
| || tmp[i].getCategory().equals(Compression.class) |
| || tmp[i].getCategory().equals(DocumentName.class) |
| || tmp[i].getCategory().equals(RequestingUserName.class)) |
| |
| operationAttributes.add(tmp[i]); |
| } |
| } |
| |
| /** |
| * Filters from the given attribute set the job template attributes |
| * out and adds them to the job attributes set. |
| * |
| * @param set the attributes to filter, may not be <code>null</code>. |
| */ |
| public void addAndFilterJobTemplateAttributes(AttributeSet set) |
| { |
| if (jobAttributes == null) |
| jobAttributes = new HashAttributeSet(); |
| |
| // document-natural-language - not defined in JPS attributes |
| // document-format - specified outside, special treatment |
| Attribute[] tmp = set.toArray(); |
| for (int i = 0; i < tmp.length; i++) |
| { |
| if (tmp[i].getCategory().equals(JobPriority.class) |
| || tmp[i].getCategory().equals(JobHoldUntil.class) |
| || tmp[i].getCategory().equals(JobSheets.class) |
| || tmp[i].getCategory().equals(MultipleDocumentHandling.class) |
| || tmp[i].getCategory().equals(Copies.class) |
| || tmp[i].getCategory().equals(Finishings.class) |
| || tmp[i].getCategory().equals(PageRanges.class) |
| || tmp[i].getCategory().equals(NumberUp.class) |
| || tmp[i].getCategory().equals(OrientationRequested.class) |
| || tmp[i].getCategory().equals(Media.class) |
| || tmp[i].getCategory().equals(PrinterResolution.class) |
| || tmp[i].getCategory().equals(PrintQuality.class) |
| || tmp[i].getCategory().equals(SheetCollate.class) |
| || tmp[i].getCategory().equals(Sides.class)) |
| |
| jobAttributes.add(tmp[i]); |
| } |
| } |
| |
| /** |
| * Does some validation of the supplied parameters and then |
| * sends the request to the ipp server or service. |
| * |
| * @return The response if any. |
| * |
| * @throws IllegalStateException if request is already sent |
| * @throws IppException if connection or request failed. |
| * @throws IOException if writing of the header, attributes or footer fails. |
| */ |
| public IppResponse send() throws IppException, IOException |
| { |
| if (alreadySent) |
| throw new IllegalStateException("Request is already sent"); |
| |
| alreadySent = true; |
| |
| OutputStream stream = connection.getOutputStream(); |
| DataOutputStream out = new DataOutputStream(stream); |
| |
| // the header 8 bytes long |
| out.writeShort(VERSION); |
| out.writeShort(operation_id); |
| out.writeInt(request_id); |
| |
| logger.log(Component.IPP, "OperationID: " + Integer.toHexString(operation_id) |
| + " RequestID: " + request_id); |
| |
| // Pass stuff the the attribute writer which knows how to |
| // write the attributes in correct order |
| logger.log(Component.IPP, "Operation Attributes"); |
| |
| RequestWriter writer = new RequestWriter(out); |
| writer.writeOperationAttributes(operationAttributes); |
| |
| if (jobAttributes != null) |
| { |
| logger.log(Component.IPP, "Job Attributes"); |
| out.write(IppDelimiterTag.JOB_ATTRIBUTES_TAG); |
| writer.writeAttributes(jobAttributes); |
| } |
| if (printerAttributes != null) |
| { |
| logger.log(Component.IPP, "Printer Attributes"); |
| out.write(IppDelimiterTag.PRINTER_ATTRIBUTES_TAG); |
| writer.writeAttributes(printerAttributes); |
| } |
| |
| // write the delimiter to the data |
| out.write(IppDelimiterTag.END_OF_ATTRIBUTES_TAG); |
| |
| // check if data is byte[] or inputstream |
| if (data instanceof InputStream) |
| { |
| byte[] readbuf = new byte[2048]; |
| int len = 0; |
| while( (len = ((InputStream) data).read(readbuf)) > 0) |
| out.write(readbuf, 0, len); |
| } |
| else if (data != null) |
| { |
| out.write((byte[]) data); |
| } |
| |
| out.flush(); |
| stream.flush(); |
| |
| // Set the connection timeout, for if the printer is offline. |
| // FIXME: The print services polling should probably be done in its |
| // own thread. |
| connection.setConnectTimeout( timeout ); |
| |
| int responseCode = connection.getResponseCode(); |
| |
| if (responseCode == HttpURLConnection.HTTP_OK) |
| { |
| IppResponse response = new IppResponse(requestUri, operation_id); |
| response.setResponseData(connection.getInputStream()); |
| return response; |
| } |
| |
| logger.log(Component.IPP, "HTTP-Statuscode: " + responseCode); |
| |
| throw new IppException("Request failed got HTTP status code " |
| + responseCode); |
| } |
| |
| } |