Initial import.
[clinton/mirror/jspi/.git] / jspi / src / main / java / de / lohndirekt / print / IppRequestCupsImpl.java
1 /**
2 * Copyright (C) 2004 <a href="http://www.lohndirekt.de/">lohndirekt.de</a>
3 *
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.
8 *
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.
13 *
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
17 *
18 */
19 package de.lohndirekt.print;
20
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;
28 import java.net.URI;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.Vector;
32 import java.util.logging.Level;
33 import java.util.logging.Logger;
34
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;
41
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;
51
52 /**
53 * @author bpusch, speters, sefftinge
54 *
55 */
56 class IppRequestCupsImpl implements IppRequest {
57 class IppResponseIppImpl implements IppResponse {
58 private Logger log = Logger.getLogger(this.getClass().getName());
59
60 private IppStatus status;
61
62 private Map attributes;
63
64 IppResponseIppImpl(InputStream response) {
65 try{
66 parseResponse(response);
67 } catch (IOException e) {
68 log.log(Level.SEVERE, e.getMessage(), e);
69 throw new RuntimeException(e);
70 }
71
72 }
73
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)
78 + (int) header[3]);
79 if (response.available() != 0) {
80 this.attributes = AttributeParser.parseResponse(response);
81 } else {
82 this.attributes = new HashMap();
83 }
84 if (log.isLoggable(Level.FINEST)) {
85 log.finest("Status: " + status.getText());
86 }
87 }
88
89 /**
90 * @return
91 */
92 public Map getAttributes() {
93 return attributes;
94 }
95
96 /**
97 * @return
98 */
99 public IppStatus getStatus() {
100 return status;
101 }
102
103 }
104
105 private IppConnection conn;
106
107 private boolean sent = false;
108
109 private Object data;
110
111 private URI path;
112
113 private OperationsSupported operation;
114
115 //Id wird in der Cups-API zwar übergeben, ist aber auch immer 1.
116 private int id = 1;
117
118 private PrintJobAttributeSet jobAttributes = new HashPrintJobAttributeSet();
119
120 private AttributeSet operationAttributes = new HashAttributeSet();
121
122 private AttributeSet printerAttributes = new HashAttributeSet();
123
124 private Logger log = Logger.getLogger(this.getClass().getName());
125
126 private static final int SEND_REQUEST_COUNT = 3;
127
128 private static final int SEND_REQUEST_TIMEOUT = 50;
129
130 private static final NaturalLanguage NATURAL_LANGUAGE_DEFAULT = NaturalLanguage.EN;
131
132 private static final Charset CHARSET_DEFAULT = Charset.UTF_8;
133
134
135 /**
136 * @param operation
137 */
138 IppRequestCupsImpl(URI path, OperationsSupported operation) {
139 this.path = path;
140 this.operation = operation;
141 init();
142 }
143
144 /**
145 * @param printerAttributes
146 */
147 public void setPrinterAttributes(AttributeSet attrs) {
148 this.printerAttributes = attrs;
149 }
150
151 /**
152 *
153 */
154 private void init() {
155 setStandardAttributes();
156 }
157
158 /**
159 *
160 */
161 private void setStandardAttributes() {
162 operationAttributes.add(CHARSET_DEFAULT);
163 operationAttributes.add(NATURAL_LANGUAGE_DEFAULT);
164 }
165
166 /**
167 *
168 */
169 private byte[] ippFooter() {
170 byte[] footer = new byte[1];
171 footer[0] = (byte) IppDelimiterTag.END_ATTRIBUTES.getValue();
172 return footer;
173 }
174
175 /**
176 *
177 */
178 private byte[] ippAttributes() throws UnsupportedEncodingException {
179 ByteArrayOutputStream out = new ByteArrayOutputStream();
180 operationAttributes(out);
181 printerAttributes(out);
182 jobAttributes(out);
183 byte[] body = out.toByteArray();
184 return body;
185 }
186
187 /**
188 * @param out
189 * @return
190 */
191 private void jobAttributes(ByteArrayOutputStream out) throws UnsupportedEncodingException {
192 if (!jobAttributes.isEmpty()) {
193 out.write((byte) IppDelimiterTag.BEGIN_JOB_ATTRIBUTES
194 .getValue());
195 for (int i = 0; i < jobAttributes.toArray().length; i++) {
196 Attribute attribute = (Attribute) jobAttributes.toArray()[i];
197 AttributeWriter.attributeBytes(attribute, out);
198 }
199 }
200 }
201
202 /**
203 *
204 * @param out
205 * @return
206 */
207 private void printerAttributes(ByteArrayOutputStream out) throws UnsupportedEncodingException {
208 if (!printerAttributes.isEmpty()) {
209 out.write((byte) IppDelimiterTag.BEGIN_PRINTER_ATTRIBUTES
210 .getValue());
211 Attribute[] attributes = printerAttributes.toArray();
212 for (int i = 0; i < attributes.length; i++) {
213 AttributeWriter.attributeBytes(attributes[i], out);
214 }
215 }
216 }
217
218 /**
219 *
220 * @param out
221 * @return
222 */
223 private void operationAttributes(ByteArrayOutputStream out) throws UnsupportedEncodingException {
224 if (!operationAttributes.isEmpty()) {
225 out.write((byte) IppDelimiterTag.BEGIN_OPERATION_ATTRIBUTES
226 .getValue());
227 Attribute[] attributes = AttributeHelper.getOrderedOperationAttributeArray(operationAttributes);
228 for (int i = 0; i < attributes.length; i++) {
229 AttributeWriter.attributeBytes(attributes[i], out);
230 }
231 }
232 }
233
234 /**
235 *
236 */
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
242 out.write((byte) 1);
243 //minor version-number
244 out.write((byte) 1);
245 //2 byte operation id
246 AttributeWriter.writeInt2(this.operation.getValue(), out);
247 //4 byte request id
248 AttributeWriter.writeInt4(this.id, out);
249 return out.toByteArray();
250 }
251
252 /**
253 * @param attributes
254 */
255 public void addOperationAttributes(AttributeSet attributes) {
256 this.operationAttributes.addAll(attributes);
257 }
258
259 /**
260 * @param stream
261 */
262 public void setData(InputStream data) {
263 this.data = data;
264 }
265
266 public void setData(byte[] data) {
267 this.data = data;
268 }
269
270 /**
271 * @param attributes
272 */
273 public void setJobAttributes(PrintJobAttributeSet attributes) {
274 this.jobAttributes = attributes;
275 }
276
277 /**
278 * @see de.lohndirekt.print.IppRequest#send()
279 * @throws IllegalArgumentException
280 * when called twice
281 */
282 public IppResponse send() throws IOException {
283 if (sent) {
284 throw new IllegalStateException("Send must not be called twice");
285 }
286
287 String username = findUserName(this.operationAttributes);
288 String password = findPassword(this.operationAttributes);
289 boolean ok = false;
290 int tries = 0;
291 while (!ok && tries < SEND_REQUEST_COUNT) {
292 tries++;
293
294 this.conn = new IppHttpConnection(this.path, username, password);
295
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());
302 }
303 SequenceInputStream stream = new SequenceInputStream(v.elements());
304 conn.setIppRequest(stream);
305 conn.execute();
306
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
312 + " ms";
313 }
314 log.warning(msg);
315 }
316 try {
317 Thread.sleep(50);
318 } catch (InterruptedException e) {
319 if (log.isLoggable(Level.INFO)) {
320 log.info(e.getMessage());
321 }
322 }
323 } else {
324 ok = true;
325 }
326
327 }
328 this.sent = true;
329 return getResponse();
330 }
331
332 /**
333 * @param list
334 * @return
335 */
336 private String findUserName(AttributeSet list) {
337 if (list != null) {
338 TextSyntax attr = (TextSyntax) list.get(IppAttributeName.REQUESTING_USER_NAME
339 .getCategory());
340 if (attr != null) {
341 return attr.getValue();
342 }
343 }
344 return null;
345 }
346
347 /**
348 * @param list
349 * @return
350 */
351 private String findPassword(AttributeSet list) {
352 if (list != null) {
353 TextSyntax attr = (TextSyntax) list.get(IppAttributeName.REQUESTING_USER_PASSWD
354 .getCategory());
355 if (attr != null) {
356 return attr.getValue();
357 }
358 }
359 return null;
360 }
361
362 /**
363 * @return
364 */
365 private InputStream getDataAsInputStream() {
366 if (data == null) {
367 return null;
368 // } else if (data instanceof FileInputStream) {
369 // FileInputStream in = (FileInputStream) data;
370 // try {
371 // if (in.getFD().valid()){
372 // //TODO remove this hack
373 // in=new FileInputStream("./testfiles/test.pdf");
374 // }
375 // } catch (IOException e) {
376 // log.log(Level.WARNING, "", e);
377 // }
378 // return in;
379 } else if (data instanceof InputStream) {
380 InputStream in = (InputStream) data;
381 return in;
382 } else if (data instanceof byte[]) {
383 return new ByteArrayInputStream((byte[]) data);
384 } else {
385 throw new IllegalStateException("unknown data format : "
386 + data.getClass());
387 }
388 }
389
390 private IppResponse getResponse() throws IOException {
391 if (this.conn.getStatusCode()==HttpURLConnection.HTTP_OK) {
392 return new IppResponseIppImpl(conn.getIppResponse());
393 } else {
394 return null;
395 }
396 }
397
398 }