| 1 | /**\r |
| 2 | * Copyright (C) 2003 <a href="http://www.lohndirekt.de/">lohndirekt.de</a>\r |
| 3 | *\r |
| 4 | * This library is free software; you can redistribute it and/or\r |
| 5 | * modify it under the terms of the GNU Lesser General Public\r |
| 6 | * License as published by the Free Software Foundation; either\r |
| 7 | * version 2.1 of the License, or (at your option) any later version.\r |
| 8 | * \r |
| 9 | * This library is distributed in the hope that it will be useful,\r |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of\r |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r |
| 12 | * Lesser General Public License for more details.\r |
| 13 | * \r |
| 14 | * You should have received a copy of the GNU Lesser General Public\r |
| 15 | * License along with this library; if not, write to the Free Software\r |
| 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r |
| 17 | * \r |
| 18 | */\r |
| 19 | package de.lohndirekt.print;\r |
| 20 | \r |
| 21 | import java.io.IOException;\r |
| 22 | import java.net.URI;\r |
| 23 | import java.net.URISyntaxException;\r |
| 24 | import java.util.ArrayList;\r |
| 25 | import java.util.HashMap;\r |
| 26 | import java.util.Iterator;\r |
| 27 | import java.util.List;\r |
| 28 | import java.util.Map;\r |
| 29 | import java.util.Set;\r |
| 30 | import java.util.logging.Level;\r |
| 31 | import java.util.logging.Logger;\r |
| 32 | \r |
| 33 | import javax.print.Doc;\r |
| 34 | import javax.print.DocPrintJob;\r |
| 35 | import javax.print.PrintException;\r |
| 36 | import javax.print.PrintService;\r |
| 37 | import javax.print.attribute.Attribute;\r |
| 38 | import javax.print.attribute.AttributeSet;\r |
| 39 | import javax.print.attribute.HashAttributeSet;\r |
| 40 | import javax.print.attribute.HashPrintJobAttributeSet;\r |
| 41 | import javax.print.attribute.PrintJobAttribute;\r |
| 42 | import javax.print.attribute.PrintJobAttributeSet;\r |
| 43 | import javax.print.attribute.PrintRequestAttributeSet;\r |
| 44 | import javax.print.event.PrintJobAttributeListener;\r |
| 45 | import javax.print.event.PrintJobEvent;\r |
| 46 | import javax.print.event.PrintJobListener;\r |
| 47 | \r |
| 48 | import de.lohndirekt.print.attribute.AttributeHelper;\r |
| 49 | import de.lohndirekt.print.attribute.IppAttributeName;\r |
| 50 | import de.lohndirekt.print.attribute.IppStatus;\r |
| 51 | import de.lohndirekt.print.attribute.ipp.jobdesc.JobId;\r |
| 52 | import de.lohndirekt.print.attribute.ipp.jobdesc.JobUri;\r |
| 53 | import de.lohndirekt.print.attribute.ipp.printerdesc.supported.OperationsSupported;\r |
| 54 | \r |
| 55 | /**\r |
| 56 | * @author bpusch\r |
| 57 | *\r |
| 58 | */\r |
| 59 | class Job implements DocPrintJob {\r |
| 60 | protected boolean ok = false;\r |
| 61 | protected Logger log = Logger.getLogger(this.getClass().getName());\r |
| 62 | protected IppPrintService printService;\r |
| 63 | private PrintJobAttributeSet jobAttributes;\r |
| 64 | protected JobUri jobUri;\r |
| 65 | private List jobListeners;\r |
| 66 | private Map attributeListeners;\r |
| 67 | \r |
| 68 | /**\r |
| 69 | * @param service\r |
| 70 | */\r |
| 71 | public Job(IppPrintService service) {\r |
| 72 | this.printService = service;\r |
| 73 | }\r |
| 74 | \r |
| 75 | /**\r |
| 76 | *\r |
| 77 | */\r |
| 78 | public PrintService getPrintService() {\r |
| 79 | return this.printService;\r |
| 80 | }\r |
| 81 | \r |
| 82 | /**\r |
| 83 | *\r |
| 84 | */\r |
| 85 | public PrintJobAttributeSet getAttributes() {\r |
| 86 | return this.jobAttributes;\r |
| 87 | }\r |
| 88 | \r |
| 89 | /**\r |
| 90 | *\r |
| 91 | */\r |
| 92 | public void print(Doc doc, PrintRequestAttributeSet attributes) throws PrintException {\r |
| 93 | IppRequest request = null;\r |
| 94 | request = this.printService.request(OperationsSupported.PRINT_JOB);\r |
| 95 | try {\r |
| 96 | request.setData(doc.getStreamForBytes());\r |
| 97 | } catch (IOException e) {\r |
| 98 | log.log(Level.SEVERE, e.getMessage(), e);\r |
| 99 | throw new PrintException("Error getting document data (" + description(attributes) + "): " + e.getMessage());\r |
| 100 | }\r |
| 101 | //add the operation attributes\r |
| 102 | AttributeSet operationAttributes = new HashAttributeSet();\r |
| 103 | operationAttributes.addAll(AttributeHelper.jobOperationAttributes(attributes));\r |
| 104 | operationAttributes.addAll(AttributeHelper.docOperationAttributes(doc));\r |
| 105 | request.addOperationAttributes(operationAttributes);\r |
| 106 | //set the job template attributes\r |
| 107 | request.setJobAttributes(AttributeHelper.jobAttributes(attributes));\r |
| 108 | IppResponse response = null;\r |
| 109 | try {\r |
| 110 | response = request.send();\r |
| 111 | notifyJobListeners(PrintJobEvent.DATA_TRANSFER_COMPLETE);\r |
| 112 | } catch (IOException e) {\r |
| 113 | log.log(Level.SEVERE, e.getMessage(), e);\r |
| 114 | throw new PrintException("Error sending " + description(attributes) + " to IPP service: " + e.getMessage());\r |
| 115 | }\r |
| 116 | if (response != null && response.getStatus()!=null) {\r |
| 117 | Map responseAttributes = response.getAttributes();\r |
| 118 | updateAttributes(responseAttributes);\r |
| 119 | if (response.getStatus().equals(IppStatus.SUCCESSFUL_OK)\r |
| 120 | || response.getStatus().equals(IppStatus.SUCCESSFUL_OK_CONFLICTING_ATTRIBUTES)\r |
| 121 | || response.getStatus().equals(IppStatus.SUCCESSFUL_OK_IGNORED_OR_SUBSTITUTED_ATTRIBUTES)) {\r |
| 122 | if (responseAttributes.containsKey(IppAttributeName.JOB_URI.getCategory())) {\r |
| 123 | Set jobUriList = (Set) responseAttributes.get(IppAttributeName.JOB_URI.getCategory());\r |
| 124 | this.jobUri = (JobUri) jobUriList.iterator().next();\r |
| 125 | }\r |
| 126 | notifyJobListeners(PrintJobEvent.JOB_COMPLETE);\r |
| 127 | this.ok = true;\r |
| 128 | } else {\r |
| 129 | notifyJobListeners(PrintJobEvent.JOB_FAILED);\r |
| 130 | this.ok = false;\r |
| 131 | }\r |
| 132 | } else {\r |
| 133 | notifyJobListeners(PrintJobEvent.JOB_FAILED);\r |
| 134 | this.ok = false;\r |
| 135 | }\r |
| 136 | notifyJobListeners(PrintJobEvent.NO_MORE_EVENTS);\r |
| 137 | if (!this.ok){\r |
| 138 | String msg = "Printing " + description(attributes) + " failed";\r |
| 139 | if (response != null && response.getStatus()!=null) {\r |
| 140 | msg+=": Server status was '"+response.getStatus().getStatus()+" - "+response.getStatus().getText()+"'!";\r |
| 141 | }\r |
| 142 | throw new PrintException(msg);\r |
| 143 | }\r |
| 144 | }\r |
| 145 | \r |
| 146 | \r |
| 147 | \r |
| 148 | public void addPrintJobListener(PrintJobListener listener) {\r |
| 149 | if (listener != null) {\r |
| 150 | if (jobListeners == null) {\r |
| 151 | jobListeners = new ArrayList();\r |
| 152 | }\r |
| 153 | jobListeners.add(listener);\r |
| 154 | }\r |
| 155 | }\r |
| 156 | \r |
| 157 | public void removePrintJobListener(PrintJobListener listener) {\r |
| 158 | if (listener != null) {\r |
| 159 | jobListeners.remove(listener);\r |
| 160 | }\r |
| 161 | }\r |
| 162 | \r |
| 163 | public void addPrintJobAttributeListener(PrintJobAttributeListener listener, PrintJobAttributeSet attributes) {\r |
| 164 | if (listener != null) {\r |
| 165 | if (attributeListeners == null) {\r |
| 166 | attributeListeners = new HashMap();\r |
| 167 | }\r |
| 168 | attributeListeners.put(listener, attributes);\r |
| 169 | }\r |
| 170 | }\r |
| 171 | \r |
| 172 | public void removePrintJobAttributeListener(PrintJobAttributeListener listener) {\r |
| 173 | if (listener != null) {\r |
| 174 | attributeListeners.remove(listener);\r |
| 175 | }\r |
| 176 | }\r |
| 177 | \r |
| 178 | void notifyJobListeners(int eventType) {\r |
| 179 | if (jobListeners != null) {\r |
| 180 | PrintJobEvent event = new PrintJobEvent(this, eventType);\r |
| 181 | for (Iterator iter = jobListeners.iterator(); iter.hasNext();) {\r |
| 182 | PrintJobListener listener = (PrintJobListener) iter.next();\r |
| 183 | if (eventType == PrintJobEvent.DATA_TRANSFER_COMPLETE) {\r |
| 184 | listener.printDataTransferCompleted(event);\r |
| 185 | } else if (eventType == PrintJobEvent.JOB_CANCELED) {\r |
| 186 | listener.printJobCanceled(event);\r |
| 187 | } else if (eventType == PrintJobEvent.JOB_COMPLETE) {\r |
| 188 | listener.printJobCompleted(event);\r |
| 189 | } else if (eventType == PrintJobEvent.JOB_FAILED) {\r |
| 190 | listener.printJobFailed(event);\r |
| 191 | } else if (eventType == PrintJobEvent.NO_MORE_EVENTS) {\r |
| 192 | listener.printJobNoMoreEvents(event);\r |
| 193 | } else if (eventType == PrintJobEvent.REQUIRES_ATTENTION) {\r |
| 194 | listener.printJobRequiresAttention(event);\r |
| 195 | }\r |
| 196 | }\r |
| 197 | }\r |
| 198 | }\r |
| 199 | \r |
| 200 | IppResponse sendRequest(OperationsSupported operation, AttributeSet operationAttributes) throws IOException {\r |
| 201 | IppRequest request = this.request(operation);\r |
| 202 | request.addOperationAttributes(operationAttributes);\r |
| 203 | IppResponse response = null;\r |
| 204 | response = request.send();\r |
| 205 | return response;\r |
| 206 | }\r |
| 207 | \r |
| 208 | private IppRequest request(OperationsSupported operation) {\r |
| 209 | IppRequest request = IppRequestFactory.createIppRequest(this.jobUri.getURI(), operation, this.printService.getRequestingUserName(), this.printService.getRequestingUserPassword());\r |
| 210 | AttributeSet operationAttributes = new HashAttributeSet();\r |
| 211 | operationAttributes.add(this.jobUri);\r |
| 212 | request.addOperationAttributes(operationAttributes);\r |
| 213 | return request;\r |
| 214 | }\r |
| 215 | \r |
| 216 | // public methods which are not part of the JPS API\r |
| 217 | /**\r |
| 218 | * \r |
| 219 | * This method returns the Job with the given JobId that is held by \r |
| 220 | * the given PrintService.\r |
| 221 | * <br>\r |
| 222 | * This method might return a Job which is not hold by this PrintService\r |
| 223 | * but the same CUPS server \r |
| 224 | * \r |
| 225 | * @param service\r |
| 226 | * @param id\r |
| 227 | * @return the corresponding Job wihth the given JobId, or null if no such \r |
| 228 | * Job exists\r |
| 229 | * @throws PrintException\r |
| 230 | */\r |
| 231 | static Job getJob(IppPrintService service, JobId id) throws PrintException {\r |
| 232 | Job job = new Job(service);\r |
| 233 | job.printService = service;\r |
| 234 | URI jobUri;\r |
| 235 | try {\r |
| 236 | jobUri =\r |
| 237 | new URI(\r |
| 238 | service.getUri().getScheme(),\r |
| 239 | service.getUri().getAuthority(),\r |
| 240 | "/jobs/" + id.getValue(),\r |
| 241 | service.getUri().getQuery(),\r |
| 242 | service.getUri().getFragment());\r |
| 243 | } catch (URISyntaxException e) {\r |
| 244 | throw new PrintException("Internal bug.", e);\r |
| 245 | }\r |
| 246 | job.jobUri = new JobUri(jobUri);\r |
| 247 | try {\r |
| 248 | job.updateAttributes();\r |
| 249 | } catch (IllegalStateException e) {\r |
| 250 | job = null;\r |
| 251 | }\r |
| 252 | return job;\r |
| 253 | }\r |
| 254 | \r |
| 255 | /**\r |
| 256 | * @param responseAttributes\r |
| 257 | */\r |
| 258 | private void updateAttributes(Map responseAttributes) {\r |
| 259 | this.jobAttributes = new HashPrintJobAttributeSet();\r |
| 260 | for (Iterator iter = responseAttributes.values().iterator(); iter.hasNext();) {\r |
| 261 | Set values = (Set) iter.next();\r |
| 262 | for (Iterator listIter = values.iterator(); listIter.hasNext();) {\r |
| 263 | Attribute attribute = (Attribute) listIter.next();\r |
| 264 | if (attribute instanceof PrintJobAttribute)\r |
| 265 | this.jobAttributes.add(attribute);\r |
| 266 | }\r |
| 267 | }\r |
| 268 | }\r |
| 269 | \r |
| 270 | /**\r |
| 271 | * Updates the Job's attributes to the current values.\r |
| 272 | * \r |
| 273 | * @throws PrintException\r |
| 274 | */\r |
| 275 | private void updateAttributes() throws PrintException {\r |
| 276 | try {\r |
| 277 | AttributeSet operationAttributes = new HashAttributeSet();\r |
| 278 | IppResponse response = sendRequest(OperationsSupported.GET_JOB_ATTRIBUTES, operationAttributes);\r |
| 279 | if (!response.getStatus().equals(IppStatus.SUCCESSFUL_OK)\r |
| 280 | && !response.getStatus().equals(IppStatus.SUCCESSFUL_OK_CONFLICTING_ATTRIBUTES)\r |
| 281 | && !response.getStatus().equals(IppStatus.SUCCESSFUL_OK_IGNORED_OR_SUBSTITUTED_ATTRIBUTES)) {\r |
| 282 | if (response.getStatus().equals(IppStatus.CLIENT_ERROR_NOT_FOUND)) {\r |
| 283 | throw new IllegalStateException("Job with uri '" + this.jobUri.toString() + "does not exist.");\r |
| 284 | }\r |
| 285 | throw new PrintException("Request not successful.");\r |
| 286 | }\r |
| 287 | Map attribsMap = response.getAttributes();\r |
| 288 | updateAttributes(attribsMap);\r |
| 289 | \r |
| 290 | } catch (IOException e) {\r |
| 291 | throw new PrintException("Update failed.", e);\r |
| 292 | }\r |
| 293 | }\r |
| 294 | \r |
| 295 | private String description(PrintRequestAttributeSet attributes){\r |
| 296 | String description = "job";\r |
| 297 | if (attributes != null){\r |
| 298 | Attribute jobName = attributes.get(IppAttributeName.JOB_NAME.getCategory());\r |
| 299 | if (jobName != null){\r |
| 300 | description += " " + jobName.toString();\r |
| 301 | }\r |
| 302 | }\r |
| 303 | return description;\r |
| 304 | }\r |
| 305 | }\r |