2 * Copyright (C) 2004 <a href="http://www.lohndirekt.de/">lohndirekt.de</a>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 package de
.lohndirekt
.print
;
21 import java
.io
.ByteArrayInputStream
;
22 import java
.io
.ByteArrayOutputStream
;
23 import java
.io
.IOException
;
24 import java
.io
.InputStream
;
25 import java
.io
.SequenceInputStream
;
26 import java
.io
.UnsupportedEncodingException
;
27 import java
.net
.HttpURLConnection
;
29 import java
.util
.HashMap
;
31 import java
.util
.Vector
;
32 import java
.util
.logging
.Level
;
33 import java
.util
.logging
.Logger
;
35 import javax
.print
.attribute
.Attribute
;
36 import javax
.print
.attribute
.AttributeSet
;
37 import javax
.print
.attribute
.HashAttributeSet
;
38 import javax
.print
.attribute
.HashPrintJobAttributeSet
;
39 import javax
.print
.attribute
.PrintJobAttributeSet
;
40 import javax
.print
.attribute
.TextSyntax
;
42 import de
.lohndirekt
.print
.attribute
.AttributeHelper
;
43 import de
.lohndirekt
.print
.attribute
.AttributeParser
;
44 import de
.lohndirekt
.print
.attribute
.AttributeWriter
;
45 import de
.lohndirekt
.print
.attribute
.IppAttributeName
;
46 import de
.lohndirekt
.print
.attribute
.IppDelimiterTag
;
47 import de
.lohndirekt
.print
.attribute
.IppStatus
;
48 import de
.lohndirekt
.print
.attribute
.ipp
.Charset
;
49 import de
.lohndirekt
.print
.attribute
.ipp
.NaturalLanguage
;
50 import de
.lohndirekt
.print
.attribute
.ipp
.printerdesc
.supported
.OperationsSupported
;
53 * @author bpusch, speters, sefftinge
56 class IppRequestCupsImpl
implements IppRequest
{
57 class IppResponseIppImpl
implements IppResponse
{
58 private Logger log
= Logger
.getLogger(this.getClass().getName());
60 private IppStatus status
;
62 private Map attributes
;
64 IppResponseIppImpl(InputStream response
) {
66 parseResponse(response
);
67 } catch (IOException e
) {
68 log
.log(Level
.SEVERE
, e
.getMessage(), e
);
69 throw new RuntimeException(e
);
74 private void parseResponse(InputStream response
) throws IOException
{
75 byte[] header
= new byte[8];
76 response
.read(header
);
77 this.status
= IppStatus
.get((int) (header
[2] << 8)
79 if (response
.available() != 0) {
80 this.attributes
= AttributeParser
.parseResponse(response
);
82 this.attributes
= new HashMap();
84 if (log
.isLoggable(Level
.FINEST
)) {
85 log
.finest("Status: " + status
.getText());
92 public Map
getAttributes() {
99 public IppStatus
getStatus() {
105 private IppConnection conn
;
107 private boolean sent
= false;
113 private OperationsSupported operation
;
115 //Id wird in der Cups-API zwar übergeben, ist aber auch immer 1.
118 private PrintJobAttributeSet jobAttributes
= new HashPrintJobAttributeSet();
120 private AttributeSet operationAttributes
= new HashAttributeSet();
122 private AttributeSet printerAttributes
= new HashAttributeSet();
124 private Logger log
= Logger
.getLogger(this.getClass().getName());
126 private static final int SEND_REQUEST_COUNT
= 3;
128 private static final int SEND_REQUEST_TIMEOUT
= 50;
130 private static final NaturalLanguage NATURAL_LANGUAGE_DEFAULT
= NaturalLanguage
.EN
;
132 private static final Charset CHARSET_DEFAULT
= Charset
.UTF_8
;
138 IppRequestCupsImpl(URI path
, OperationsSupported operation
) {
140 this.operation
= operation
;
145 * @param printerAttributes
147 public void setPrinterAttributes(AttributeSet attrs
) {
148 this.printerAttributes
= attrs
;
154 private void init() {
155 setStandardAttributes();
161 private void setStandardAttributes() {
162 operationAttributes
.add(CHARSET_DEFAULT
);
163 operationAttributes
.add(NATURAL_LANGUAGE_DEFAULT
);
169 private byte[] ippFooter() {
170 byte[] footer
= new byte[1];
171 footer
[0] = (byte) IppDelimiterTag
.END_ATTRIBUTES
.getValue();
178 private byte[] ippAttributes() throws UnsupportedEncodingException
{
179 ByteArrayOutputStream out
= new ByteArrayOutputStream();
180 operationAttributes(out
);
181 printerAttributes(out
);
183 byte[] body
= out
.toByteArray();
191 private void jobAttributes(ByteArrayOutputStream out
) throws UnsupportedEncodingException
{
192 if (!jobAttributes
.isEmpty()) {
193 out
.write((byte) IppDelimiterTag
.BEGIN_JOB_ATTRIBUTES
195 for (int i
= 0; i
< jobAttributes
.toArray().length
; i
++) {
196 Attribute attribute
= (Attribute
) jobAttributes
.toArray()[i
];
197 AttributeWriter
.attributeBytes(attribute
, out
);
207 private void printerAttributes(ByteArrayOutputStream out
) throws UnsupportedEncodingException
{
208 if (!printerAttributes
.isEmpty()) {
209 out
.write((byte) IppDelimiterTag
.BEGIN_PRINTER_ATTRIBUTES
211 Attribute
[] attributes
= printerAttributes
.toArray();
212 for (int i
= 0; i
< attributes
.length
; i
++) {
213 AttributeWriter
.attributeBytes(attributes
[i
], out
);
223 private void operationAttributes(ByteArrayOutputStream out
) throws UnsupportedEncodingException
{
224 if (!operationAttributes
.isEmpty()) {
225 out
.write((byte) IppDelimiterTag
.BEGIN_OPERATION_ATTRIBUTES
227 Attribute
[] attributes
= AttributeHelper
.getOrderedOperationAttributeArray(operationAttributes
);
228 for (int i
= 0; i
< attributes
.length
; i
++) {
229 AttributeWriter
.attributeBytes(attributes
[i
], out
);
237 private byte[] ippHeader() {
238 //Ipp header data according to http://www.ietf.org/rfc/rfc2910.txt
239 ByteArrayOutputStream out
= new ByteArrayOutputStream(8);
240 //The first 2 bytes represent the IPP version number (1.1)
241 //major version-number
243 //minor version-number
245 //2 byte operation id
246 AttributeWriter
.writeInt2(this.operation
.getValue(), out
);
248 AttributeWriter
.writeInt4(this.id
, out
);
249 return out
.toByteArray();
255 public void addOperationAttributes(AttributeSet attributes
) {
256 this.operationAttributes
.addAll(attributes
);
262 public void setData(InputStream data
) {
266 public void setData(byte[] data
) {
273 public void setJobAttributes(PrintJobAttributeSet attributes
) {
274 this.jobAttributes
= attributes
;
278 * @see de.lohndirekt.print.IppRequest#send()
279 * @throws IllegalArgumentException
282 public IppResponse
send() throws IOException
{
284 throw new IllegalStateException("Send must not be called twice");
287 String username
= findUserName(this.operationAttributes
);
288 String password
= findPassword(this.operationAttributes
);
291 while (!ok
&& tries
< SEND_REQUEST_COUNT
) {
294 this.conn
= new IppHttpConnection(this.path
, username
, password
);
296 Vector v
= new Vector();
297 v
.add(new ByteArrayInputStream(this.ippHeader()));
298 v
.add(new ByteArrayInputStream(this.ippAttributes()));
299 v
.add(new ByteArrayInputStream(this.ippFooter()));
300 if (this.data
!= null) {
301 v
.add(this.getDataAsInputStream());
303 SequenceInputStream stream
= new SequenceInputStream(v
.elements());
304 conn
.setIppRequest(stream
);
307 if (conn
.getStatusCode() != HttpURLConnection
.HTTP_OK
) {
308 if (log
.isLoggable(Level
.INFO
)) {
309 String msg
= "Cups seems to be busy - STATUSCODE "+conn
.getStatusCode();
310 if (tries
< SEND_REQUEST_COUNT
) {
311 msg
+= " - going to retry in " + SEND_REQUEST_TIMEOUT
318 } catch (InterruptedException e
) {
319 if (log
.isLoggable(Level
.INFO
)) {
320 log
.info(e
.getMessage());
329 return getResponse();
336 private String
findUserName(AttributeSet list
) {
338 TextSyntax attr
= (TextSyntax
) list
.get(IppAttributeName
.REQUESTING_USER_NAME
341 return attr
.getValue();
351 private String
findPassword(AttributeSet list
) {
353 TextSyntax attr
= (TextSyntax
) list
.get(IppAttributeName
.REQUESTING_USER_PASSWD
356 return attr
.getValue();
365 private InputStream
getDataAsInputStream() {
368 // } else if (data instanceof FileInputStream) {
369 // FileInputStream in = (FileInputStream) data;
371 // if (in.getFD().valid()){
372 // //TODO remove this hack
373 // in=new FileInputStream("./testfiles/test.pdf");
375 // } catch (IOException e) {
376 // log.log(Level.WARNING, "", e);
379 } else if (data
instanceof InputStream
) {
380 InputStream in
= (InputStream
) data
;
382 } else if (data
instanceof byte[]) {
383 return new ByteArrayInputStream((byte[]) data
);
385 throw new IllegalStateException("unknown data format : "
390 private IppResponse
getResponse() throws IOException
{
391 if (this.conn
.getStatusCode()==HttpURLConnection
.HTTP_OK
) {
392 return new IppResponseIppImpl(conn
.getIppResponse());