mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			876 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			876 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Java
		
	
	
	
/* 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
 | 
						|
    {
 | 
						|
      String[] values = attribute.getValues();
 | 
						|
 | 
						|
      String name = ((Attribute) attribute).getName();
 | 
						|
      out.writeByte(IppValueTag.KEYWORD);
 | 
						|
      out.writeShort(name.length());
 | 
						|
      out.write(name.getBytes());
 | 
						|
      out.writeShort(values[0].length());
 | 
						|
      out.write(values[0].getBytes());
 | 
						|
 | 
						|
      for (int i=1; i < values.length; i++)
 | 
						|
        {
 | 
						|
          out.writeByte(IppValueTag.KEYWORD);
 | 
						|
          out.writeShort(0x0000); // length for additional value
 | 
						|
          out.writeShort(values[i].length());
 | 
						|
          out.write(values[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);
 | 
						|
      RequestedAttributes reqAttrs
 | 
						|
        = (RequestedAttributes)attributes.get(RequestedAttributes.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 if (reqAttrs != null)
 | 
						|
        {
 | 
						|
          write(reqAttrs);
 | 
						|
          attributes.remove(RequestedAttributes.class);
 | 
						|
          logger.log(Component.IPP, "RequestedAttributes: <" + reqAttrs + ">");
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          throw 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);
 | 
						|
  }
 | 
						|
 | 
						|
}
 |