IppRequest.java 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. /* IppRequest.java --
  2. Copyright (C) 2006 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package gnu.javax.print.ipp;
  32. import gnu.classpath.debug.Component;
  33. import gnu.classpath.debug.SystemLogger;
  34. import gnu.javax.print.ipp.attribute.CharsetSyntax;
  35. import gnu.javax.print.ipp.attribute.NaturalLanguageSyntax;
  36. import gnu.javax.print.ipp.attribute.RequestedAttributes;
  37. import gnu.javax.print.ipp.attribute.job.AttributesCharset;
  38. import gnu.javax.print.ipp.attribute.job.AttributesNaturalLanguage;
  39. import gnu.javax.print.ipp.attribute.job.JobId;
  40. import gnu.javax.print.ipp.attribute.job.JobUri;
  41. import gnu.javax.print.ipp.attribute.printer.DocumentFormat;
  42. import java.io.DataOutputStream;
  43. import java.io.IOException;
  44. import java.io.InputStream;
  45. import java.io.OutputStream;
  46. import java.net.HttpURLConnection;
  47. import java.net.URI;
  48. import java.net.URL;
  49. import java.util.Calendar;
  50. import java.util.Date;
  51. import java.util.GregorianCalendar;
  52. import java.util.List;
  53. import java.util.logging.Logger;
  54. import javax.print.attribute.Attribute;
  55. import javax.print.attribute.AttributeSet;
  56. import javax.print.attribute.DateTimeSyntax;
  57. import javax.print.attribute.EnumSyntax;
  58. import javax.print.attribute.HashAttributeSet;
  59. import javax.print.attribute.IntegerSyntax;
  60. import javax.print.attribute.ResolutionSyntax;
  61. import javax.print.attribute.SetOfIntegerSyntax;
  62. import javax.print.attribute.TextSyntax;
  63. import javax.print.attribute.URISyntax;
  64. import javax.print.attribute.standard.Compression;
  65. import javax.print.attribute.standard.Copies;
  66. import javax.print.attribute.standard.DocumentName;
  67. import javax.print.attribute.standard.Fidelity;
  68. import javax.print.attribute.standard.Finishings;
  69. import javax.print.attribute.standard.JobHoldUntil;
  70. import javax.print.attribute.standard.JobImpressions;
  71. import javax.print.attribute.standard.JobKOctets;
  72. import javax.print.attribute.standard.JobMediaSheets;
  73. import javax.print.attribute.standard.JobName;
  74. import javax.print.attribute.standard.JobOriginatingUserName;
  75. import javax.print.attribute.standard.JobPriority;
  76. import javax.print.attribute.standard.JobSheets;
  77. import javax.print.attribute.standard.Media;
  78. import javax.print.attribute.standard.MultipleDocumentHandling;
  79. import javax.print.attribute.standard.NumberUp;
  80. import javax.print.attribute.standard.OrientationRequested;
  81. import javax.print.attribute.standard.PageRanges;
  82. import javax.print.attribute.standard.PrintQuality;
  83. import javax.print.attribute.standard.PrinterResolution;
  84. import javax.print.attribute.standard.PrinterURI;
  85. import javax.print.attribute.standard.RequestingUserName;
  86. import javax.print.attribute.standard.SheetCollate;
  87. import javax.print.attribute.standard.Sides;
  88. /**
  89. * <code>IppRequest</code> models a request to an IPP compatible
  90. * server as described in RFC 2910 - IPP/1.1: Encoding and Transport.
  91. * <p>
  92. * The byte stream is structured as follows (for an official description
  93. * please have a look at the RFC document mentioned above):
  94. * <ul>
  95. * <li>version-number - 2 bytes - required</li>
  96. * <li>operation-id - 2 bytes - required</li>
  97. * <li>request-id - 4 bytes - required</li>
  98. * <li>attribute-group - n bytes - 0 or more</li>
  99. * <li>end-of-attributes-tag - 1 byte - required</li>
  100. * <li>data - q bytes - optional</li>
  101. * </ul>
  102. * </p>
  103. *
  104. * @author Wolfgang Baer (WBaer@gmx.de)
  105. */
  106. public class IppRequest
  107. {
  108. /**
  109. * The printer-poll timeout.
  110. */
  111. private static final int timeout = 1000;
  112. /**
  113. * Helper class used to write the attributes of a request
  114. * into the supplied data output stream in the correct way.
  115. *
  116. * @author Wolfgang Baer (WBaer@gmx.de)
  117. */
  118. class RequestWriter
  119. {
  120. private DataOutputStream out;
  121. /**
  122. * Creates a RequestWriter.
  123. *
  124. * @param stream the stream to write to.
  125. */
  126. RequestWriter(DataOutputStream stream)
  127. {
  128. out = stream;
  129. }
  130. /**
  131. * Writes an attribute in IntegerSyntax into the stream.
  132. * @param attribute the attribute
  133. * @throws IOException if thrown by the stream
  134. */
  135. private void write(IntegerSyntax attribute) throws IOException
  136. {
  137. String name = ((Attribute) attribute).getName();
  138. out.writeByte(IppValueTag.INTEGER);
  139. out.writeShort(name.length());
  140. out.write(name.getBytes());
  141. out.writeShort(4); // length, integer is 4 bytes
  142. out.writeInt(attribute.getValue());
  143. }
  144. /**
  145. * Writes an attribute in EnumSyntax into the stream.
  146. * @param attribute the attribute
  147. * @throws IOException if thrown by the stream
  148. */
  149. private void write(EnumSyntax attribute) throws IOException
  150. {
  151. // in JPS API enum syntax is used for enums, keyword and boolean types
  152. String name = ((Attribute) attribute).getName();
  153. // the enum value types
  154. if (attribute instanceof Finishings
  155. || attribute instanceof OrientationRequested
  156. || attribute instanceof PrintQuality)
  157. {
  158. out.writeByte(IppValueTag.ENUM);
  159. out.writeShort(name.length());
  160. out.write(name.getBytes());
  161. out.writeShort(4); // length, enum is 4 bytes
  162. out.writeInt(attribute.getValue());
  163. }
  164. // the boolean value type
  165. else if (attribute instanceof Fidelity)
  166. {
  167. out.writeByte(IppValueTag.BOOLEAN);
  168. out.writeShort(name.length());
  169. out.write(name.getBytes());
  170. out.writeShort(1); // length, boolean is 1 bytes
  171. out.writeByte(attribute.getValue() == 0 ? 0x00 : 0x01);
  172. }
  173. // the keyword value types
  174. else
  175. {
  176. String keyword = attribute.toString();
  177. out.writeByte(IppValueTag.KEYWORD);
  178. out.writeShort(name.length());
  179. out.write(name.getBytes());
  180. out.writeShort(keyword.length());
  181. out.write(keyword.getBytes());
  182. }
  183. }
  184. /**
  185. * Writes an attribute in SetOfIntegerSyntax into the stream.
  186. * @param attribute the attribute
  187. * @throws IOException if thrown by the stream
  188. */
  189. private void write(SetOfIntegerSyntax attribute) throws IOException
  190. {
  191. String name = ((Attribute) attribute).getName();
  192. int[][] ranges = attribute.getMembers();
  193. for (int i = 0; i < ranges.length; i++)
  194. {
  195. out.writeByte(IppValueTag.RANGEOFINTEGER);
  196. if (i == 0)
  197. {
  198. out.writeShort(name.length());
  199. out.write(name.getBytes());
  200. }
  201. else
  202. out.writeShort(0x0000); // only name-length
  203. out.writeShort(8); // range is 8 bytes
  204. out.writeInt(ranges[i][0]);
  205. out.writeInt(ranges[i][1]);
  206. }
  207. }
  208. /**
  209. * Writes an attribute in ResolutionSyntax into the stream.
  210. * @param attribute the attribute
  211. * @throws IOException if thrown by the stream
  212. */
  213. private void write(ResolutionSyntax attribute) throws IOException
  214. {
  215. String name = ((Attribute) attribute).getName();
  216. out.writeByte(IppValueTag.RESOLUTION);
  217. out.writeShort(name.length());
  218. out.write(name.getBytes());
  219. out.writeShort(9); // length fixed to 9
  220. out.writeInt(attribute.getCrossFeedResolution(ResolutionSyntax.DPI));
  221. out.writeInt(attribute.getFeedResolution(ResolutionSyntax.DPI));
  222. out.writeByte(ResolutionSyntax.DPI);
  223. }
  224. /**
  225. * Writes an attribute in DateTimeSyntax into the stream.
  226. * <p>
  227. * The syntax value is defined as 11 octets follwing the
  228. * DateAndTime format of RFC 1903. (see IppResponse)
  229. * </p>
  230. *
  231. * @param attribute the attribute
  232. * @throws IOException if thrown by the stream
  233. */
  234. private void write(DateTimeSyntax attribute) throws IOException
  235. {
  236. String name = ((Attribute) attribute).getName();
  237. out.writeByte(IppValueTag.DATETIME);
  238. out.writeShort(name.length());
  239. out.write(name.getBytes());
  240. out.writeShort(11); // length fixed to 11
  241. Date date = attribute.getValue();
  242. Calendar cal = new GregorianCalendar();
  243. cal.setTime(date);
  244. out.writeShort(cal.get(Calendar.YEAR));
  245. out.writeByte(cal.get(Calendar.MONTH));
  246. out.writeByte(cal.get(Calendar.DAY_OF_MONTH));
  247. out.writeByte(cal.get(Calendar.HOUR_OF_DAY));
  248. out.writeByte(cal.get(Calendar.MINUTE));
  249. int second = cal.get(Calendar.SECOND);
  250. out.writeByte(second == 0 ? 60 : second);
  251. out.writeByte(cal.get(Calendar.MILLISECOND) / 100);
  252. int offsetInMillis = cal.get(Calendar.ZONE_OFFSET);
  253. char directionFromUTC = '+';
  254. if (offsetInMillis < 0)
  255. {
  256. directionFromUTC = '-';
  257. offsetInMillis = offsetInMillis * (-1);
  258. }
  259. out.writeByte(directionFromUTC);
  260. out.writeByte(offsetInMillis / 3600000); // hours
  261. out.writeByte((offsetInMillis % 3600000) / 60000); // minutes
  262. }
  263. /**
  264. * Writes an attribute in TextSyntax into the stream.
  265. * <p>
  266. * By default attributes are qritten as TEXT_WITHOUT_LANGUAGE value-tag.
  267. * As some attributes in the JPS are TextSyntax attributes but actually
  268. * of NAME value-tag in IPP this method checks for these attributes and
  269. * writes them as NAME_WITHOUT_LANGUAGE value-tag into the stream.
  270. * </p>
  271. *
  272. * @param attribute the attribute
  273. * @param out the stream to write to
  274. * @throws IOException if thrown by the stream
  275. */
  276. private void write(TextSyntax attribute) throws IOException
  277. {
  278. // We only use *WithoutLanguage, correct according to spec.
  279. String name = ((Attribute) attribute).getName();
  280. if (attribute instanceof RequestingUserName
  281. || attribute instanceof JobName
  282. || attribute instanceof DocumentName
  283. || attribute instanceof JobOriginatingUserName)
  284. out.writeByte(IppValueTag.NAME_WITHOUT_LANGUAGE);
  285. else if (attribute instanceof DocumentFormat)
  286. out.writeByte(IppValueTag.MIME_MEDIA_TYPE);
  287. else
  288. out.writeByte(IppValueTag.TEXT_WITHOUT_LANGUAGE);
  289. out.writeShort(name.length());
  290. out.write(name.getBytes());
  291. out.writeShort(attribute.getValue().length());
  292. out.write(attribute.getValue().getBytes());
  293. }
  294. /**
  295. * Writes an attribute in URISyntax into the stream.
  296. * @param attribute the attribute
  297. * @param out the stream to write to
  298. * @throws IOException if thrown by the stream
  299. */
  300. private void write(URISyntax attribute) throws IOException
  301. {
  302. // only uriScheme syntax type should not appear
  303. // in a request (reference-uri-schemes-supported)
  304. String name = ((Attribute) attribute).getName();
  305. String uriAscii = attribute.getURI().toASCIIString();
  306. out.writeByte(IppValueTag.URI);
  307. out.writeShort(name.length());
  308. out.write(name.getBytes());
  309. out.writeShort(uriAscii.length());
  310. out.write(uriAscii.getBytes());
  311. }
  312. /**
  313. * Writes an attribute in CharsetSyntax into the stream.
  314. * @param attribute the attribute
  315. * @param out the stream to write to
  316. * @throws IOException if thrown by the stream
  317. */
  318. private void write(CharsetSyntax attribute) throws IOException
  319. {
  320. String name = ((Attribute) attribute).getName();
  321. out.writeByte(IppValueTag.CHARSET);
  322. out.writeShort(name.length());
  323. out.write(name.getBytes());
  324. out.writeShort(attribute.getValue().length());
  325. out.write(attribute.getValue().getBytes());
  326. }
  327. /**
  328. * Writes an attribute in NaturalLanguageSyntax into the stream.
  329. * @param attribute the attribute
  330. * @param out the stream to write to
  331. * @throws IOException if thrown by the stream
  332. */
  333. private void write(NaturalLanguageSyntax attribute) throws IOException
  334. {
  335. String name = ((Attribute) attribute).getName();
  336. out.writeByte(IppValueTag.NATURAL_LANGUAGE);
  337. out.writeShort(name.length());
  338. out.write(name.getBytes());
  339. out.writeShort(attribute.getValue().length());
  340. out.write(attribute.getValue().getBytes());
  341. }
  342. /**
  343. * Writes an attribute in RequestedAttributes into the stream.
  344. * @param attribute the attribute
  345. * @param out the stream to write to
  346. * @throws IOException if thrown by the stream
  347. */
  348. private void write(RequestedAttributes attribute) throws IOException
  349. {
  350. String[] values = attribute.getValues();
  351. String name = ((Attribute) attribute).getName();
  352. out.writeByte(IppValueTag.KEYWORD);
  353. out.writeShort(name.length());
  354. out.write(name.getBytes());
  355. out.writeShort(values[0].length());
  356. out.write(values[0].getBytes());
  357. for (int i=1; i < values.length; i++)
  358. {
  359. out.writeByte(IppValueTag.KEYWORD);
  360. out.writeShort(0x0000); // length for additional value
  361. out.writeShort(values[i].length());
  362. out.write(values[i].getBytes());
  363. }
  364. }
  365. /**
  366. * Writes the given operation attribute group of the given map instance
  367. * (key=group, values=set of attributes) into the supplied data
  368. * output stream.
  369. *
  370. * @param attributes the set with the attributes.
  371. *
  372. * @throws IOException if thrown by the used DataOutputStream.
  373. * @throws IppException if unknown attributes occur.
  374. */
  375. public void writeOperationAttributes(AttributeSet attributes)
  376. throws IOException, IppException
  377. {
  378. out.write(IppDelimiterTag.OPERATION_ATTRIBUTES_TAG);
  379. // its essential to write these two in this order and as first ones
  380. Attribute att = attributes.get(AttributesCharset.class);
  381. write((CharsetSyntax) att);
  382. logger.log(Component.IPP, "Attribute: Name: <"
  383. + att.getCategory().getName() + "> Value: <" + att.toString() + ">");
  384. attributes.remove(AttributesCharset.class);
  385. att = attributes.get(AttributesNaturalLanguage.class);
  386. write((NaturalLanguageSyntax) att);
  387. attributes.remove(AttributesNaturalLanguage.class);
  388. logger.log(Component.IPP, "Attribute: Name: <"
  389. + att.getCategory().getName() + "> Value: <" + att.toString() + ">");
  390. // furthermore its essential to now write out the target attribute
  391. PrinterURI printerUri = (PrinterURI) attributes.get(PrinterURI.class);
  392. JobUri jobUri = (JobUri) attributes.get(JobUri.class);
  393. JobId jobId = (JobId) attributes.get(JobId.class);
  394. RequestedAttributes reqAttrs
  395. = (RequestedAttributes)attributes.get(RequestedAttributes.class);
  396. if (printerUri != null && jobId == null && jobUri == null)
  397. {
  398. write(printerUri);
  399. attributes.remove(PrinterURI.class);
  400. logger.log(Component.IPP, "Attribute: Name: <" + printerUri
  401. .getCategory().getName() + "> Value: <" + printerUri.toString() + ">");
  402. }
  403. else if (jobUri != null && jobId == null && printerUri == null)
  404. {
  405. write(jobUri);
  406. attributes.remove(JobUri.class);
  407. logger.log(Component.IPP, "Attribute: Name: <" + jobUri
  408. .getCategory().getName() + "> Value: <" + jobUri.toString() + ">");
  409. }
  410. else if (printerUri != null && jobId != null && jobUri == null)
  411. {
  412. write(printerUri); // must be third
  413. write(jobId);
  414. attributes.remove(PrinterURI.class);
  415. attributes.remove(JobId.class);
  416. logger.log(Component.IPP, "Attribute: Name: <" + printerUri
  417. .getCategory().getName() + "> Value: <" + printerUri.toString() + ">");
  418. logger.log(Component.IPP, "Attribute: Name: <" + jobId.getCategory()
  419. .getName() + "> Value: <" + jobId.toString() + ">");
  420. }
  421. else if (jobUri != null && jobId != null)
  422. {
  423. write(jobUri);
  424. attributes.remove(JobUri.class);
  425. attributes.remove(JobId.class); // MUST NOT redundant
  426. logger.log(Component.IPP, "Attribute: Name: <" + jobUri.getCategory()
  427. .getName() + "> Value: <" + jobUri.toString() + ">");
  428. }
  429. else if (reqAttrs != null)
  430. {
  431. write(reqAttrs);
  432. attributes.remove(RequestedAttributes.class);
  433. logger.log(Component.IPP, "RequestedAttributes: <" + reqAttrs + ">");
  434. }
  435. else
  436. {
  437. throw new IppException("Unknown target operation attribute combination.");
  438. }
  439. writeAttributes(attributes);
  440. }
  441. /**
  442. * Writes the given attribute groups of the given map instance
  443. * (key=group, values=set of attributes) into the supplied data
  444. * output stream.
  445. *
  446. * @param attributes the set with the attributes.
  447. *
  448. * @throws IOException if thrown by the used DataOutputStream.
  449. * @throws IppException if unknown attributes occur.
  450. */
  451. public void writeAttributes(AttributeSet attributes)
  452. throws IOException, IppException
  453. {
  454. Attribute[] attributeArray = attributes.toArray();
  455. for (int i = 0; i < attributeArray.length; i++)
  456. {
  457. logger.log(Component.IPP, "Attribute: Name: <" + attributeArray[i]
  458. .getCategory().getName() + "> Value: <"
  459. + attributeArray[i].toString() + ">");
  460. if (attributeArray[i] instanceof IntegerSyntax)
  461. write((IntegerSyntax) attributeArray[i]);
  462. else if (attributeArray[i] instanceof TextSyntax)
  463. write((TextSyntax) attributeArray[i]);
  464. else if (attributeArray[i] instanceof DateTimeSyntax)
  465. write((DateTimeSyntax) attributeArray[i]);
  466. else if (attributeArray[i] instanceof ResolutionSyntax)
  467. write((ResolutionSyntax) attributeArray[i]);
  468. else if (attributeArray[i] instanceof SetOfIntegerSyntax)
  469. write((SetOfIntegerSyntax) attributeArray[i]);
  470. else if (attributeArray[i] instanceof EnumSyntax)
  471. write((EnumSyntax) attributeArray[i]);
  472. else if (attributeArray[i] instanceof URISyntax)
  473. write((URISyntax) attributeArray[i]);
  474. else if (attributeArray[i] instanceof CharsetSyntax)
  475. write((CharsetSyntax) attributeArray[i]);
  476. else if (attributeArray[i] instanceof NaturalLanguageSyntax)
  477. write((NaturalLanguageSyntax) attributeArray[i]);
  478. else if (attributeArray[i] instanceof RequestedAttributes)
  479. write((RequestedAttributes) attributeArray[i]);
  480. else
  481. throw new IppException("Unknown syntax type");
  482. }
  483. }
  484. }
  485. /**
  486. * Logger for tracing - enable by passing
  487. * -Dgnu.classpath.debug.components=ipp to the vm.
  488. */
  489. static final Logger logger = SystemLogger.SYSTEM;
  490. /**
  491. * The request id counter simply counts up
  492. * to give unique request ids per JVM instance.
  493. */
  494. private static int requestIdCounter = 1;
  495. /** The IPP version defaults to 1.1 */
  496. private static final short VERSION = 0x0101;
  497. /** Signals if the request is already on its way */
  498. private boolean alreadySent = false;
  499. /** The operation type of this request. */
  500. private short operation_id;
  501. /**
  502. * The request id of this request. This is
  503. * assigned automatically by the constructor.
  504. */
  505. private final int request_id;
  506. private AttributeSet operationAttributes;
  507. private AttributeSet printerAttributes;
  508. private AttributeSet jobAttributes;
  509. private Object data;
  510. private URI requestUri;
  511. /** The underlying connection - IPP is http based */
  512. private HttpURLConnection connection;
  513. /**
  514. * Creates an IPPRequest instance.
  515. *
  516. * @param uri the URI of the request
  517. * @param user the user if any
  518. * @param password the password of the supplied user
  519. */
  520. public IppRequest(URI uri, String user, String password)
  521. {
  522. request_id = incrementRequestIdCounter();
  523. requestUri = uri;
  524. try
  525. {
  526. URL url = new URL("http",
  527. user == null
  528. ? uri.getHost() : user + ":"
  529. + password + "@" + uri.getHost(),
  530. uri.getPort(), uri.getPath());
  531. connection = (HttpURLConnection) url.openConnection();
  532. connection.setRequestMethod("POST");
  533. connection.setDoOutput(true);
  534. connection.setRequestProperty("Content-type", "application/ipp");
  535. connection.setRequestProperty("Accept", "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2");
  536. }
  537. catch (IOException e)
  538. {
  539. // MalformedURLException - uri is already checked
  540. // ProtocolException - POST is correct method type
  541. // IOException -HTTPURLConnection constructor actually
  542. // does never throw this exception.
  543. logger.log(Component.IPP, "Unexpected IOException", e);
  544. }
  545. logger.log(Component.IPP, "[IppConnection] Host: " + uri.getHost()
  546. + " Port: " + uri.getPort() + " Path: "
  547. + uri.getPath());
  548. }
  549. /**
  550. * Synchronized method to be called by the constructor
  551. * to assign a unique request id to this request.
  552. *
  553. * @return The unique request id.
  554. */
  555. private synchronized int incrementRequestIdCounter()
  556. {
  557. return IppRequest.requestIdCounter++;
  558. }
  559. /**
  560. * Returns the id of this request.
  561. *
  562. * @return The request ID.
  563. */
  564. public int getRequestID()
  565. {
  566. return request_id;
  567. }
  568. /**
  569. * Sets the data of the request. The data used in this
  570. * request will be the one of the supplied inputstream
  571. * instead of the alternative byte array possibility.
  572. *
  573. * @param stream the input stream to use for the data.
  574. */
  575. public void setData(InputStream stream)
  576. {
  577. data = stream;
  578. }
  579. /**
  580. * Sets the data of the request. The data used in this
  581. * request will be the one of the supplied byte[]
  582. * instead of the alternative input stream possibility.
  583. *
  584. * @param bytes the byte[] to use for the data.
  585. */
  586. public void setData(byte[] bytes)
  587. {
  588. data = bytes;
  589. }
  590. /**
  591. * Sets the operation id for this request.
  592. *
  593. * @param id the operation id.
  594. */
  595. public void setOperationID(short id)
  596. {
  597. operation_id = id;
  598. }
  599. /**
  600. * Adds the default values for the operation
  601. * attributes "attributes-charset" and
  602. * "attributes-natural-language"
  603. */
  604. public void setOperationAttributeDefaults()
  605. {
  606. if (operationAttributes == null)
  607. operationAttributes = new HashAttributeSet();
  608. operationAttributes.add(AttributesCharset.UTF8);
  609. operationAttributes.add(AttributesNaturalLanguage.EN);
  610. }
  611. /**
  612. * Add the job attribute of this request to the given
  613. * attribute set.
  614. *
  615. * @param attribute the job attribute.
  616. */
  617. public void addJobAttribute(Attribute attribute)
  618. {
  619. if (jobAttributes == null)
  620. jobAttributes = new HashAttributeSet();
  621. jobAttributes.add(attribute);
  622. }
  623. /**
  624. * Sets the printer attribute of this request to the given
  625. * attribute set.
  626. *
  627. * @param attribute the printer attribute.
  628. */
  629. public void addPrinterAttributes(Attribute attribute)
  630. {
  631. if (printerAttributes == null)
  632. printerAttributes = new HashAttributeSet();
  633. printerAttributes.add(attribute);
  634. }
  635. /**
  636. * Adds the given attribute to the operation attributes set.
  637. *
  638. * @param attribute the operation attribute to add.
  639. */
  640. public void addOperationAttribute(Attribute attribute)
  641. {
  642. if (operationAttributes == null)
  643. operationAttributes = new HashAttributeSet();
  644. operationAttributes.add(attribute);
  645. }
  646. /**
  647. * Filters from the given attribute set the job operation out
  648. * and adds them to the operation attributes set.
  649. *
  650. * @param set the attributes to filter, may not be <code>null</code>.
  651. */
  652. public void addAndFilterJobOperationAttributes(AttributeSet set)
  653. {
  654. if (operationAttributes == null)
  655. operationAttributes = new HashAttributeSet();
  656. // document-natural-language - not defined in JPS attributes
  657. // document-format - specified outside, special treatment
  658. Attribute[] tmp = set.toArray();
  659. for (int i = 0; i < tmp.length; i++)
  660. {
  661. if (tmp[i].getCategory().equals(JobName.class)
  662. || tmp[i].getCategory().equals(Fidelity.class)
  663. || tmp[i].getCategory().equals(JobImpressions.class)
  664. || tmp[i].getCategory().equals(JobKOctets.class)
  665. || tmp[i].getCategory().equals(JobMediaSheets.class)
  666. || tmp[i].getCategory().equals(Compression.class)
  667. || tmp[i].getCategory().equals(DocumentName.class)
  668. || tmp[i].getCategory().equals(RequestingUserName.class))
  669. operationAttributes.add(tmp[i]);
  670. }
  671. }
  672. /**
  673. * Filters from the given attribute set the job template attributes
  674. * out and adds them to the job attributes set.
  675. *
  676. * @param set the attributes to filter, may not be <code>null</code>.
  677. */
  678. public void addAndFilterJobTemplateAttributes(AttributeSet set)
  679. {
  680. if (jobAttributes == null)
  681. jobAttributes = new HashAttributeSet();
  682. // document-natural-language - not defined in JPS attributes
  683. // document-format - specified outside, special treatment
  684. Attribute[] tmp = set.toArray();
  685. for (int i = 0; i < tmp.length; i++)
  686. {
  687. if (tmp[i].getCategory().equals(JobPriority.class)
  688. || tmp[i].getCategory().equals(JobHoldUntil.class)
  689. || tmp[i].getCategory().equals(JobSheets.class)
  690. || tmp[i].getCategory().equals(MultipleDocumentHandling.class)
  691. || tmp[i].getCategory().equals(Copies.class)
  692. || tmp[i].getCategory().equals(Finishings.class)
  693. || tmp[i].getCategory().equals(PageRanges.class)
  694. || tmp[i].getCategory().equals(NumberUp.class)
  695. || tmp[i].getCategory().equals(OrientationRequested.class)
  696. || tmp[i].getCategory().equals(Media.class)
  697. || tmp[i].getCategory().equals(PrinterResolution.class)
  698. || tmp[i].getCategory().equals(PrintQuality.class)
  699. || tmp[i].getCategory().equals(SheetCollate.class)
  700. || tmp[i].getCategory().equals(Sides.class))
  701. jobAttributes.add(tmp[i]);
  702. }
  703. }
  704. /**
  705. * Does some validation of the supplied parameters and then
  706. * sends the request to the ipp server or service.
  707. *
  708. * @return The response if any.
  709. *
  710. * @throws IllegalStateException if request is already sent
  711. * @throws IppException if connection or request failed.
  712. * @throws IOException if writing of the header, attributes or footer fails.
  713. */
  714. public IppResponse send() throws IppException, IOException
  715. {
  716. if (alreadySent)
  717. throw new IllegalStateException("Request is already sent");
  718. alreadySent = true;
  719. OutputStream stream = connection.getOutputStream();
  720. DataOutputStream out = new DataOutputStream(stream);
  721. // the header 8 bytes long
  722. out.writeShort(VERSION);
  723. out.writeShort(operation_id);
  724. out.writeInt(request_id);
  725. logger.log(Component.IPP, "OperationID: " + Integer.toHexString(operation_id)
  726. + " RequestID: " + request_id);
  727. // Pass stuff the the attribute writer which knows how to
  728. // write the attributes in correct order
  729. logger.log(Component.IPP, "Operation Attributes");
  730. RequestWriter writer = new RequestWriter(out);
  731. writer.writeOperationAttributes(operationAttributes);
  732. if (jobAttributes != null)
  733. {
  734. logger.log(Component.IPP, "Job Attributes");
  735. out.write(IppDelimiterTag.JOB_ATTRIBUTES_TAG);
  736. writer.writeAttributes(jobAttributes);
  737. }
  738. if (printerAttributes != null)
  739. {
  740. logger.log(Component.IPP, "Printer Attributes");
  741. out.write(IppDelimiterTag.PRINTER_ATTRIBUTES_TAG);
  742. writer.writeAttributes(printerAttributes);
  743. }
  744. // write the delimiter to the data
  745. out.write(IppDelimiterTag.END_OF_ATTRIBUTES_TAG);
  746. // check if data is byte[] or inputstream
  747. if (data instanceof InputStream)
  748. {
  749. byte[] readbuf = new byte[2048];
  750. int len = 0;
  751. while( (len = ((InputStream) data).read(readbuf)) > 0)
  752. out.write(readbuf, 0, len);
  753. }
  754. else if (data != null)
  755. {
  756. out.write((byte[]) data);
  757. }
  758. out.flush();
  759. stream.flush();
  760. // Set the connection timeout, for if the printer is offline.
  761. // FIXME: The print services polling should probably be done in its
  762. // own thread.
  763. connection.setConnectTimeout( timeout );
  764. int responseCode = connection.getResponseCode();
  765. if (responseCode == HttpURLConnection.HTTP_OK)
  766. {
  767. IppResponse response = new IppResponse(requestUri, operation_id);
  768. response.setResponseData(connection.getInputStream());
  769. return response;
  770. }
  771. logger.log(Component.IPP, "HTTP-Statuscode: " + responseCode);
  772. throw new IppException("Request failed got HTTP status code "
  773. + responseCode);
  774. }
  775. }