]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
113729df2d5324c9d16ae3c07e8b393d2094e709
[tmp/jakarta-migration.git] /
1 /**     \r
2  * NuxeoImageUtils.java\r
3  *\r
4  * {Purpose of This Class}\r
5  *\r
6  * {Other Notes Relating to This Class (Optional)}\r
7  *\r
8  * $LastChangedBy: $\r
9  * $LastChangedRevision: $\r
10  * $LastChangedDate: $\r
11  *\r
12  * This document is a part of the source code and related artifacts\r
13  * for CollectionSpace, an open source collections management system\r
14  * for museums and related institutions:\r
15  *\r
16  * http://www.collectionspace.org\r
17  * http://wiki.collectionspace.org\r
18  *\r
19  * Copyright © 2009 {Contributing Institution}.\r
20  *\r
21  * Licensed under the Educational Community License (ECL), Version 2.0.\r
22  * You may not use this file except in compliance with this License.\r
23  *\r
24  * You may obtain a copy of the ECL 2.0 License at\r
25  * https://source.collectionspace.org/collection-space/LICENSE.txt\r
26  */\r
27 package org.collectionspace.services.common.imaging.nuxeo;\r
28 \r
29 import java.io.File;\r
30 import java.io.ByteArrayOutputStream;\r
31 import java.io.FileNotFoundException;\r
32 import java.io.InputStream;\r
33 import java.io.BufferedInputStream;\r
34 import java.io.IOException;\r
35 import java.io.Serializable;\r
36 import java.math.BigDecimal;\r
37 import java.math.BigInteger;\r
38 import java.util.HashMap;\r
39 import java.util.List;\r
40 import java.util.Map;\r
41 \r
42 import org.nuxeo.runtime.api.Framework;\r
43 //import org.nuxeo.runtime.api.ServiceManager;\r
44 //import org.nuxeo.runtime.api.ServiceDescriptor;\r
45 \r
46 //import org.nuxeo.common.utils.FileUtils;\r
47 \r
48 import org.nuxeo.ecm.platform.picture.api.ImageInfo;\r
49 import org.nuxeo.ecm.platform.picture.api.ImagingDocumentConstants;\r
50 import org.nuxeo.ecm.platform.picture.api.ImagingService;\r
51 import org.nuxeo.ecm.platform.picture.api.PictureView;\r
52 \r
53 import org.nuxeo.ecm.platform.mimetype.MimetypeDetectionException;\r
54 import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry;\r
55 import org.nuxeo.ecm.platform.picture.api.adapters.PictureBlobHolder;\r
56 import org.nuxeo.ecm.platform.filemanager.api.FileManager;\r
57 import org.nuxeo.ecm.platform.types.TypeManager;\r
58 \r
59 import org.nuxeo.ecm.core.repository.RepositoryDescriptor;\r
60 import org.nuxeo.ecm.core.repository.RepositoryManager;\r
61 \r
62 import org.nuxeo.ecm.core.repository.RepositoryService;\r
63 //import org.nuxeo.runtime.model.ComponentManager;\r
64 //import org.nuxeo.runtime.model.impl.ComponentManagerImpl;\r
65 //import org.nuxeo.ecm.core.api.ejb.DocumentManagerBean;\r
66 //import org.nuxeo.ecm.core.storage.sql.RepositoryImpl;\r
67 //import org.nuxeo.ecm.core.storage.sql.Repository;\r
68 import org.nuxeo.ecm.core.storage.sql.Binary;\r
69 import org.nuxeo.ecm.core.storage.sql.BinaryManager;\r
70 import org.nuxeo.ecm.core.storage.sql.DefaultBinaryManager;\r
71 import org.nuxeo.ecm.core.storage.sql.RepositoryImpl;\r
72 import org.nuxeo.ecm.core.storage.sql.RepositoryResolver;\r
73 import org.nuxeo.ecm.core.storage.sql.coremodel.SQLBlob;\r
74 import org.nuxeo.ecm.core.storage.sql.coremodel.SQLRepository;\r
75 //import org.nuxeo.ecm.core.storage.sql.RepositoryDescriptor;\r
76 \r
77 //import org.nuxeo.ecm.core.api.DocumentResolver;\r
78 import org.nuxeo.ecm.core.api.IdRef;\r
79 import org.nuxeo.ecm.core.api.blobholder.BlobHolder;\r
80 import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder;\r
81 import org.nuxeo.ecm.core.api.impl.DocumentModelImpl;\r
82 import org.nuxeo.ecm.core.api.impl.blob.FileBlob;\r
83 import org.nuxeo.ecm.core.api.impl.blob.StreamingBlob;\r
84 import org.nuxeo.ecm.core.api.impl.blob.ByteArrayBlob;\r
85 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;\r
86 import org.nuxeo.ecm.core.api.repository.Repository;\r
87 import org.nuxeo.ecm.core.api.Blob;\r
88 import org.nuxeo.ecm.core.api.ClientException;\r
89 import org.nuxeo.ecm.core.api.DocumentModel;\r
90 import org.nuxeo.ecm.core.api.DocumentRef;\r
91 \r
92 import org.nuxeo.ecm.core.schema.DocumentType;\r
93 import org.nuxeo.ecm.core.schema.SchemaManager;\r
94 import org.nuxeo.ecm.core.schema.types.Schema;\r
95 \r
96 import org.slf4j.Logger;\r
97 import org.slf4j.LoggerFactory;\r
98 //import org.nuxeo.ecm.core.repository.jcr.testing.RepositoryOSGITestCase;\r
99 \r
100 import org.collectionspace.services.common.ServiceMain;\r
101 import org.collectionspace.services.common.blob.BlobInput;\r
102 import org.collectionspace.services.common.context.ServiceContext;\r
103 import org.collectionspace.services.common.api.GregorianCalendarDateTimeUtils;\r
104 import org.collectionspace.services.blob.BlobsCommon;\r
105 import org.collectionspace.services.blob.DimensionSubGroup;\r
106 import org.collectionspace.services.blob.DimensionSubGroupList;\r
107 import org.collectionspace.services.blob.MeasuredPartGroup;\r
108 import org.collectionspace.services.blob.MeasuredPartGroupList;\r
109 //import org.collectionspace.services.blob.BlobsCommonList;\r
110 //import org.collectionspace.services.blob.BlobsCommonList.BlobListItem;\r
111 import org.collectionspace.services.jaxb.BlobJAXBSchema;\r
112 import org.collectionspace.services.nuxeo.client.java.CommonList;\r
113 import org.collectionspace.services.nuxeo.extension.thumbnail.ThumbnailConstants;\r
114 import org.collectionspace.services.nuxeo.util.NuxeoUtils;\r
115 import org.collectionspace.services.common.blob.BlobOutput;\r
116 \r
117 import org.collectionspace.services.config.service.ListResultField;\r
118 \r
119 \r
120 /**\r
121  * The Class NuxeoImageUtils.\r
122  */\r
123 public class NuxeoImageUtils {\r
124                 \r
125         /** The Constant logger. */\r
126         private static final Logger logger = LoggerFactory\r
127                         .getLogger(NuxeoImageUtils.class);\r
128 \r
129         public static final String DOCUMENT_PLACEHOLDER_IMAGE = "documentImage.jpg";\r
130         public static final String DOCUMENT_MISSING_PLACEHOLDER_IMAGE = "documentImageMissing.jpg";\r
131         public static final String MIME_JPEG = "image/jpeg";\r
132         /*\r
133          * FIXME: REM - These constants should be coming from configuration and NOT\r
134          * hard coded.\r
135          */\r
136         public static final String DERIVATIVE_ORIGINAL = "Original";\r
137         public static final String DERIVATIVE_ORIGINAL_TAG = DERIVATIVE_ORIGINAL\r
138                         + "_";\r
139 \r
140         public static final String DERIVATIVE_ORIGINAL_JPEG = "OriginalJpeg";\r
141         public static final String DERIVATIVE_ORIGINAL_JPEG_TAG = DERIVATIVE_ORIGINAL_JPEG\r
142                         + "_";\r
143 \r
144         public static final String DERIVATIVE_MEDIUM = "Medium";\r
145         public static final String DERIVATIVE_MEDIUM_TAG = DERIVATIVE_MEDIUM + "_";\r
146 \r
147         public static final String DERIVATIVE_THUMBNAIL = "Thumbnail";\r
148         public static final String DERIVATIVE_THUMBNAIL_TAG = DERIVATIVE_THUMBNAIL\r
149                         + "_";\r
150 \r
151         public static final String DERIVATIVE_UNKNOWN = "_UNKNOWN_DERIVATIVE_NAME_";\r
152 \r
153         //\r
154         // Image Dimension fields\r
155         //\r
156         public static final String PART_IMAGE = "digitalImage";\r
157         public static final String PART_SUMMARY = "The dimensions of a digital image -width, height, and pixel depth.";\r
158         public static final String WIDTH = "width";\r
159         public static final String HEIGHT = "height";\r
160         public static final String DEPTH = "depth";\r
161         public static final String UNIT_PIXELS = "pixels";\r
162         public static final String UNIT_BITS = "bits";\r
163         //\r
164         // Image Metadata schemas - These are Nuxeo defined schemas\r
165         //\r
166         public static final String SCHEMA_IPTC = "iptc";\r
167         public static final String SCHEMA_IMAGE_METADATA = "image_metadata";\r
168 \r
169         private static final int THUMB_SIZE_HEIGHT = 100;\r
170 \r
171         private static final int THUMB_SIZE_WIDTH = 75;\r
172 \r
173         // static DefaultBinaryManager binaryManager = new DefaultBinaryManager();\r
174         // //can we get this from Nuxeo? i.e.,\r
175         // Framework.getService(BinaryManger.class)\r
176 \r
177         // /** The temp file name. */\r
178         // static String tempFileName = "sunset.jpg";\r
179         //\r
180         // /** The file separator. */\r
181         // static String fileSeparator = System.getProperty("file.separator");\r
182         //\r
183         // /** The cur dir. */\r
184         // static String curDir = System.getProperty("user.dir");\r
185 \r
186         /**\r
187          * Instantiates a new nuxeo image utils.\r
188          */\r
189         NuxeoImageUtils() {\r
190                 // empty constructor\r
191         }\r
192 \r
193         /*\r
194          * Use this for debugging Nuxeo's PictureView class\r
195          */\r
196         private static String toStringPictureView(PictureView pictureView) {\r
197                 StringBuffer strBuffer = new StringBuffer();\r
198                 strBuffer.append("Description: " + pictureView.getDescription() + '\n');\r
199                 strBuffer.append("FileName: " + pictureView.getFilename() + '\n');\r
200                 strBuffer.append("Height: " + pictureView.getHeight() + '\n');\r
201                 strBuffer.append("Width: " + pictureView.getWidth() + '\n');\r
202                 strBuffer.append("Tag: " + pictureView.getTag() + '\n');\r
203                 strBuffer.append("Title: " + pictureView.getTitle() + '\n');\r
204                 return strBuffer.toString();\r
205         }\r
206 \r
207         // FIXME: REM - This needs to be configuration-bases and NOT hard coded!\r
208         // FIXME: REM - Use MultiviewPicture adapter to get some of this information\r
209         static private String getDerivativeUri(String uri, String derivativeName) {\r
210                 String result = DERIVATIVE_UNKNOWN;\r
211 \r
212                 if (derivativeName.startsWith(DERIVATIVE_ORIGINAL_TAG) == true) {\r
213                         result = DERIVATIVE_ORIGINAL;\r
214                 } else if (derivativeName.startsWith(DERIVATIVE_ORIGINAL_JPEG_TAG) == true) {\r
215                         result = DERIVATIVE_ORIGINAL_JPEG;\r
216                 } else if (derivativeName.startsWith(DERIVATIVE_MEDIUM_TAG) == true) {\r
217                         result = DERIVATIVE_MEDIUM;\r
218                 } else if (derivativeName.startsWith(DERIVATIVE_THUMBNAIL_TAG) == true) {\r
219                         result = DERIVATIVE_THUMBNAIL;\r
220                 }\r
221 \r
222                 return uri + result + "/" + BlobInput.URI_CONTENT_PATH;\r
223         }\r
224 \r
225         static private HashMap<String, Object> createBlobListItem(Blob blob,\r
226                         String uri) {\r
227                 HashMap<String, Object> item = new HashMap<String, Object>();\r
228 \r
229                 String value = blob.getEncoding();\r
230                 if (value != null && !value.trim().isEmpty()) {\r
231                         item.put(BlobJAXBSchema.encoding, value);\r
232                 }\r
233                 value = Long.toString(blob.getLength());\r
234                 if (value != null && !value.trim().isEmpty()) {\r
235                         item.put(BlobJAXBSchema.length, value);\r
236                 }\r
237                 value = blob.getMimeType();\r
238                 if (value != null && !value.trim().isEmpty()) {\r
239                         item.put(BlobJAXBSchema.mimeType, value);\r
240                 }\r
241                 value = blob.getFilename();\r
242                 if (value != null && !value.trim().isEmpty()) {\r
243                         item.put(BlobJAXBSchema.name, value);\r
244                 }\r
245                 value = getDerivativeUri(uri, blob.getFilename());\r
246                 if (value != null && !value.trim().isEmpty()) {\r
247                         item.put(BlobJAXBSchema.uri, value);\r
248                 }\r
249 \r
250                 return item;\r
251         }\r
252 \r
253         static public CommonList getBlobDerivatives(RepositoryInstance repoSession,\r
254                         String repositoryId, List<ListResultField> resultsFields, String uri)\r
255                         throws Exception {\r
256                 CommonList commonList = new CommonList();\r
257                 int nFields = resultsFields.size() + 2;\r
258                 String fields[] = new String[nFields];// FIXME: REM - Patrick needs to fix this hack.  It is a "common list" issue\r
259                 fields[0] = "csid";\r
260                 fields[1] = "uri";\r
261                 for (int i = 2; i < nFields; i++) {\r
262                         ListResultField field = resultsFields.get(i - 2);\r
263                         fields[i] = field.getElement();\r
264                 }\r
265                 commonList.setFieldsReturned(fields);\r
266 \r
267                 IdRef documentRef = new IdRef(repositoryId);\r
268                 DocumentModel documentModel = repoSession.getDocument(documentRef);\r
269                 DocumentBlobHolder docBlobHolder = (DocumentBlobHolder) documentModel\r
270                                 .getAdapter(BlobHolder.class);\r
271                 List<Blob> docBlobs = docBlobHolder.getBlobs();\r
272                 // List<BlobListItem> blobListItems = result.getBlobListItem();\r
273                 HashMap<String, Object> item = null;\r
274                 for (Blob blob : docBlobs) {\r
275                         if (blob != null) {\r
276                                 item = createBlobListItem(blob, uri);\r
277                                 commonList.addItem(item);\r
278                         }\r
279                 }\r
280 \r
281                 return commonList;\r
282         }\r
283 \r
284         /*\r
285          * [dublincore, uid, picture, iptc, common, image_metadata]\r
286          */\r
287         static private Map<String, Object> getMetadata(Blob nuxeoBlob)\r
288                         throws Exception {\r
289                 ImagingService service = Framework.getService(ImagingService.class);\r
290                 Map<String, Object> metadataMap = service.getImageMetadata(nuxeoBlob);\r
291                 return metadataMap;\r
292         }\r
293 \r
294         private static String[] imageTypes = {"jpeg", "bmp", "gif", "png", "tiff", "octet-stream"};\r
295         static private boolean isImageMedia(Blob nuxeoBlob) {\r
296                 boolean result = false;\r
297                 \r
298                 String mimeType = nuxeoBlob.getMimeType().toLowerCase().trim();\r
299                 String[] parts = mimeType.split("/"); // split strings like "application/xml" into an array of two strings\r
300                 if (parts.length == 2) {\r
301                         for (String type : imageTypes) {\r
302                                 if (parts[1].equalsIgnoreCase(type)) {\r
303                                         result = true;\r
304                                         break;\r
305                                 }\r
306                         }\r
307                 }\r
308                 \r
309                 return result;\r
310         }\r
311         \r
312         static private MeasuredPartGroupList getDimensions(\r
313                         DocumentModel documentModel, Blob nuxeoBlob) {\r
314                 MeasuredPartGroupList result = null;\r
315                 \r
316                 if (isImageMedia(nuxeoBlob) == true) try {\r
317                         ImagingService service = Framework.getService(ImagingService.class);\r
318                         ImageInfo imageInfo = service.getImageInfo(nuxeoBlob);\r
319                         Map<String, Object> metadataMap = getMetadata(nuxeoBlob);\r
320 \r
321                         if (imageInfo != null) {\r
322                                 //\r
323                                 // Create a timestamp to add to all the image's dimensions\r
324                                 //\r
325                                 String valueDate = GregorianCalendarDateTimeUtils\r
326                                                 .timestampUTC();\r
327                                 \r
328                                 result = new MeasuredPartGroupList();\r
329                                 List<MeasuredPartGroup> measuredPartGroupList = \r
330                                                 (result).getMeasuredPartGroup();\r
331                                 //\r
332                                 // Create a new measured part for the "image"\r
333                                 //\r
334                                 MeasuredPartGroup mpGroup = new MeasuredPartGroup();\r
335                                 mpGroup.setMeasuredPart(PART_IMAGE);\r
336                                 mpGroup.setDimensionSummary(PART_SUMMARY);\r
337                                 mpGroup.setDimensionSubGroupList(new DimensionSubGroupList());\r
338                                 List<DimensionSubGroup> dimensionSubGroupList = mpGroup.getDimensionSubGroupList()\r
339                                                 .getDimensionSubGroup();\r
340 \r
341                                 //\r
342                                 // Set the width\r
343                                 //\r
344                                 DimensionSubGroup widthDimension = new DimensionSubGroup();\r
345                                 widthDimension.setDimension(WIDTH);\r
346                                 widthDimension.setMeasurementUnit(UNIT_PIXELS);\r
347                                 widthDimension.setValue(intToBigDecimal(imageInfo.getWidth()));\r
348                                 widthDimension.setValueDate(valueDate);\r
349                                 dimensionSubGroupList.add(widthDimension);\r
350                                 //\r
351                                 // Set the height\r
352                                 //\r
353                                 DimensionSubGroup heightDimension = new DimensionSubGroup();\r
354                                 heightDimension.setDimension(HEIGHT);\r
355                                 heightDimension.setMeasurementUnit(UNIT_PIXELS);\r
356                                 heightDimension\r
357                                                 .setValue(intToBigDecimal(imageInfo.getHeight()));\r
358                                 heightDimension.setValueDate(valueDate);\r
359                                 dimensionSubGroupList.add(heightDimension);\r
360                                 //\r
361                                 // Set the depth\r
362                                 //\r
363                                 DimensionSubGroup depthDimension = new DimensionSubGroup();\r
364                                 depthDimension.setDimension(DEPTH);\r
365                                 depthDimension.setMeasurementUnit(UNIT_BITS);\r
366                                 depthDimension.setValue(intToBigDecimal(imageInfo.getDepth()));\r
367                                 depthDimension.setValueDate(valueDate);\r
368                                 dimensionSubGroupList.add(depthDimension);\r
369                                 //\r
370                                 // Now set out result\r
371                                 //\r
372                                 measuredPartGroupList.add(mpGroup);\r
373                         } else {\r
374                                 if (logger.isWarnEnabled() == true) {\r
375                                         logger.warn("Could not synthesize a dimension list of the blob: "\r
376                                                         + documentModel.getName());\r
377                                 }\r
378                         }\r
379                 } catch (Exception e) {\r
380                         logger.warn("Could not extract image information for blob: "\r
381                                         + documentModel.getName(), e);\r
382                 }\r
383 \r
384                 return result;\r
385         }\r
386 \r
387         // FIXME: Add error checking here, as none of these calls return an\r
388         // Exception\r
389         static private BigDecimal intToBigDecimal(int i) {\r
390                 BigInteger bigint = BigInteger.valueOf(i);\r
391                 BigDecimal bigdec = new BigDecimal(bigint);\r
392                 return bigdec;\r
393         }\r
394 \r
395         static private BlobsCommon createBlobsCommon(DocumentModel documentModel,\r
396                         Blob nuxeoBlob) {\r
397                 BlobsCommon result = new BlobsCommon();\r
398 \r
399                 if (documentModel != null) {\r
400                         result.setMimeType(nuxeoBlob.getMimeType());\r
401                         result.setName(nuxeoBlob.getFilename());\r
402                         result.setLength(Long.toString(nuxeoBlob.getLength()));\r
403                         result.setRepositoryId(documentModel.getId());\r
404                         MeasuredPartGroupList measuredPartGroupList = getDimensions(\r
405                                         documentModel, nuxeoBlob);\r
406                         if (measuredPartGroupList != null) {\r
407                                 result.setMeasuredPartGroupList(measuredPartGroupList);\r
408                         }\r
409                         \r
410                         // Check to see if a thumbnail preview was created by Nuxeo\r
411             if (documentModel.hasFacet(ThumbnailConstants.THUMBNAIL_FACET)) {\r
412                         String errorMsg = null;\r
413                 String thumbnailName = null;\r
414                                 try {\r
415                                         thumbnailName = (String)documentModel.getProperty(ThumbnailConstants.THUMBNAIL_SCHEMA_NAME,\r
416                                                 ThumbnailConstants.THUMBNAIL_FILENAME_PROPERTY_NAME);\r
417                                         Blob thumbnailBlob = (Blob)documentModel.getProperty(ThumbnailConstants.THUMBNAIL_SCHEMA_NAME,\r
418                                                 ThumbnailConstants.THUMBNAIL_PROPERTY_NAME);\r
419                                 } catch (ClientException e) {\r
420                                         errorMsg = "Could not extract the name of the thumbnail preview image file.";\r
421                                         if (logger.isTraceEnabled()) {\r
422                                                 logger.trace(errorMsg, e);\r
423                                         }\r
424                                 }\r
425                                 \r
426                                 if (errorMsg == null) {\r
427                                         logger.info("A thumbnail preview was created for this document blob: " + thumbnailName);\r
428                                 } else {\r
429                                         logger.warn(errorMsg);\r
430                                 }\r
431             }\r
432                 }\r
433 \r
434                 return result;\r
435         }\r
436 \r
437         /*\r
438          * This is a prototype method that is not currently used as of 1/1/2012.  However,\r
439          * it may be useful now that we've transitioned to using an embedded Nuxeo server.\r
440          */\r
441         static private File getBlobFile(RepositoryInstance ri,\r
442                         DocumentModel documentModel, Blob blob) {\r
443                 DefaultBinaryManager binaryManager = null;\r
444                 RepositoryDescriptor descriptor = null;\r
445                 File file = null;\r
446 \r
447                 try {\r
448                         RepositoryService repositoryService1 = (RepositoryService) Framework\r
449                                         .getRuntime().getComponent(RepositoryService.NAME);\r
450 \r
451                         String repositoryName = documentModel.getRepositoryName();\r
452                         RepositoryManager repositoryManager = repositoryService1\r
453                                         .getRepositoryManager();\r
454                         descriptor = repositoryManager.getDescriptor(repositoryName);\r
455 \r
456 // Keep this code around for future work/enhancements                   \r
457 //                      binaryManager = new DefaultBinaryManager();\r
458 //\r
459 //                      File storageDir = binaryManager.getStorageDir();\r
460 //                      // SQLBlob blob = (SQLBlob)\r
461 //                      // doc.getPropertyValue("schema:blobField");\r
462 //                      File file = binaryManager.getFileForDigest(blob.getDigest(), false);\r
463 \r
464                 } catch (Exception e) {\r
465                         e.printStackTrace();\r
466                 }\r
467 \r
468 // Keep this code around for future work/enhancements\r
469 //              try {\r
470 //                      binaryManager.initialize(SQLRepository.getDescriptor(descriptor));\r
471 //              } catch (IOException e) {\r
472 //                      // TODO Auto-generated catch block\r
473 //                      e.printStackTrace();\r
474 //              } catch (Exception e) {\r
475 //                      // TODO Auto-generated catch block\r
476 //                      e.printStackTrace();\r
477 //              }\r
478 \r
479 // Keep this code around for future work/enhancements\r
480 //              File storageDir = binaryManager.getStorageDir();\r
481 //              SQLBlob blob = (SQLBlob)\r
482 //              documentModel.getPropertyValue("schema:blobField");\r
483 //              File file = binaryManager.getFileForDigest(blob.getDigest(), false);\r
484 \r
485                 return file;\r
486         }\r
487 \r
488         /**\r
489          * Returns a schema, given the name of a schema.  Possibly usefule in the future\r
490          * \r
491          * @param schemaName\r
492          *            a schema name.\r
493          * @return a schema.\r
494          */\r
495         private static Schema getSchemaFromName(String schemaName) {\r
496                 SchemaManager schemaManager = null;\r
497                 try {\r
498                         schemaManager = Framework.getService(SchemaManager.class);\r
499                 } catch (Exception e) {\r
500                         // TODO Auto-generated catch block\r
501                         e.printStackTrace();\r
502                 }\r
503                 return schemaManager != null ? schemaManager.getSchema(schemaName)\r
504                                 : null;\r
505         }\r
506 \r
507         /**\r
508          * Gets the blob.  Not in use now, but might be useful in the future.\r
509          * \r
510          * @param nuxeoSession\r
511          *            the nuxeo session\r
512          * @param id\r
513          *            the id\r
514          * @return the blob\r
515          */\r
516         static private Blob getBlob(RepositoryInstance nuxeoSession, String id) {\r
517                 Blob result = null;\r
518 \r
519                 try {\r
520                         Repository repository = nuxeoSession.getRepository();\r
521                         // binaryManager.initialize(new RepositoryDescriptor());\r
522                         // binaryManager.getBinary("a4cac052ae0281979f2dcf5ab2e61a6c");\r
523                         // DocumentResolver.resolveReference(nuxeoSession, documentRef);\r
524                         // binaryManager = repository.getBinaryManager();\r
525                         // documentModel.getr\r
526                 } catch (Exception x) {\r
527                         x.printStackTrace();\r
528                 }\r
529 \r
530                 return result;\r
531         }\r
532 \r
533         /**\r
534          * Gets the type service.  Not in use, but please keep for future reference\r
535          * \r
536          * @return the type service\r
537          * @throws ClientException\r
538          *             the client exception\r
539          */\r
540         private static TypeManager getTypeService() throws ClientException {\r
541                 TypeManager typeService = null;\r
542                 try {\r
543                         typeService = Framework.getService(TypeManager.class);\r
544                 } catch (Exception e) {\r
545                         throw new ClientException(e);\r
546                 }\r
547                 return typeService;\r
548         }\r
549 \r
550         /**\r
551          * Gets the bytes.\r
552          * \r
553          * @param fis\r
554          *            the fis\r
555          * @return the bytes\r
556          */\r
557         private static byte[] getBytes(InputStream fis) {\r
558                 ByteArrayOutputStream bos = new ByteArrayOutputStream();\r
559                 byte[] buf = new byte[128 * 1024];\r
560                 try {\r
561                         for (int readNum; (readNum = fis.read(buf)) != -1;) {\r
562                                 bos.write(buf, 0, readNum);\r
563                                 // no doubt here is 0\r
564                                 /*\r
565                                  * Writes len bytes from the specified byte array starting at\r
566                                  * offset off to this byte array output stream.\r
567                                  */\r
568                                 System.out.println("read " + readNum + " bytes,");\r
569                         }\r
570                 } catch (IOException ex) {\r
571                         logger.error(ex.getMessage(), ex);\r
572                 }\r
573                 byte[] bytes = bos.toByteArray();\r
574                 // bytes is the ByteArray we need\r
575                 return bytes;\r
576         }\r
577 \r
578         /**\r
579          * Creates the serializable blob.  We may need this code, do not remove.\r
580          * \r
581          * @param fileInputStream\r
582          *            the file input stream\r
583          * @param filename\r
584          *            the filename\r
585          * @param mimeType\r
586          *            the mime type\r
587          * @return the blob\r
588          */\r
589         private static Blob createSerializableBlob(InputStream fileInputStream,\r
590                         String filename, String mimeType) {\r
591                 Blob blob = null;\r
592                 try {\r
593                         // persisting the blob makes it possible to read the binary content\r
594                         // of the request stream several times (mimetype sniffing, digest\r
595                         // computation, core binary storage)\r
596                         byte[] bytes = getBytes(fileInputStream);\r
597                         blob = new ByteArrayBlob(bytes);\r
598                         // filename\r
599                         if (filename != null) {\r
600                                 filename = getCleanFileName(filename);\r
601                         }\r
602                         blob.setFilename(filename);\r
603                         // mimetype detection\r
604                         MimetypeRegistry mimeService = Framework\r
605                                         .getService(MimetypeRegistry.class);\r
606                         String detectedMimeType = mimeService\r
607                                         .getMimetypeFromFilenameAndBlobWithDefault(filename, blob,\r
608                                                         null);\r
609                         if (detectedMimeType == null) {\r
610                                 if (mimeType != null) {\r
611                                         detectedMimeType = mimeType;\r
612                                 } else {\r
613                                         // default\r
614                                         detectedMimeType = "application/octet-stream";\r
615                                 }\r
616                         }\r
617                         blob.setMimeType(detectedMimeType);\r
618                 } catch (MimetypeDetectionException e) {\r
619                         logger.error(String.format("could not fetch mimetype for file %s",\r
620                                         filename), e);\r
621                 } catch (Exception e) {\r
622                         logger.error("", e);\r
623                 }\r
624                 return blob;\r
625         }\r
626 \r
627         /**\r
628          * Creates a serializable blob from a stream, with filename and mimetype\r
629          * detection.\r
630          * \r
631          * <p>\r
632          * Creates an in-memory blob if data is under 64K, otherwise constructs a\r
633          * serializable FileBlob which stores data in a temporary file on the hard\r
634          * disk.\r
635          * </p>\r
636          * \r
637          * @param file\r
638          *            the input stream holding data\r
639          * @param filename\r
640          *            the file name. Will be set on the blob and will used for\r
641          *            mimetype detection.\r
642          * @param mimeType\r
643          *            the detected mimetype at upload. Can be null. Will be verified\r
644          *            by the mimetype service.\r
645          * @return the blob\r
646          */\r
647         private static Blob createStreamingBlob(File file, String filename,\r
648                         String mimeType) {\r
649                 Blob blob = null;\r
650                 try {\r
651                         // persisting the blob makes it possible to read the binary content\r
652                         // of the request stream several times (mimetype sniffing, digest\r
653                         // computation, core binary storage)\r
654                         blob = StreamingBlob.createFromFile(file, mimeType).persist();\r
655                         // filename\r
656                         if (filename != null) {\r
657                                 filename = getCleanFileName(filename);\r
658                         }\r
659                         blob.setFilename(filename);\r
660                         // mimetype detection\r
661                         MimetypeRegistry mimeService = Framework\r
662                                         .getService(MimetypeRegistry.class);\r
663                         String detectedMimeType = mimeService\r
664                                         .getMimetypeFromFilenameAndBlobWithDefault(filename, blob,\r
665                                                         null);\r
666                         if (detectedMimeType == null) {\r
667                                 if (mimeType != null) {\r
668                                         detectedMimeType = mimeType;\r
669                                 } else {\r
670                                         // default\r
671                                         detectedMimeType = "application/octet-stream";\r
672                                 }\r
673                         }\r
674                         blob.setMimeType(detectedMimeType);\r
675                 } catch (MimetypeDetectionException e) {\r
676                         logger.error(String.format("could not fetch mimetype for file %s",\r
677                                         filename), e);\r
678                 } catch (IOException e) {\r
679                         logger.error("", e);\r
680                 } catch (Exception e) {\r
681                         logger.error("", e);\r
682                 }\r
683                 return blob;\r
684         }\r
685 \r
686         private static Blob createNuxeoFileBasedBlob(File file) {\r
687                 Blob result = null;\r
688 \r
689                 result = new FileBlob(file);\r
690                 return result;\r
691         }\r
692 \r
693         /**\r
694          * Returns a clean filename, stripping upload path on client side.\r
695          * <p>\r
696          * Fixes NXP-544\r
697          * </p>\r
698          * \r
699          * @param filename\r
700          *            the filename\r
701          * @return the clean file name\r
702          */\r
703         private static String getCleanFileName(String filename) {\r
704                 String res = null;\r
705                 int lastWinSeparator = filename.lastIndexOf('\\');\r
706                 int lastUnixSeparator = filename.lastIndexOf('/');\r
707                 int lastSeparator = Math.max(lastWinSeparator, lastUnixSeparator);\r
708                 if (lastSeparator != -1) {\r
709                         res = filename.substring(lastSeparator + 1, filename.length());\r
710                 } else {\r
711                         res = filename;\r
712                 }\r
713                 return res;\r
714         }\r
715 \r
716         /**\r
717          * Gets Nuxeo's file manager service.\r
718          * \r
719          * @return the file manager service\r
720          * @throws ClientException\r
721          *             the client exception\r
722          */\r
723         private static FileManager getFileManagerService() throws ClientException {\r
724                 FileManager result = null;\r
725                 try {\r
726                         result = Framework.getService(FileManager.class);\r
727                 } catch (Exception e) {\r
728                         String msg = "Unable to get Nuxeo's FileManager service.";\r
729                         logger.error(msg, e);\r
730                         throw new ClientException("msg", e);\r
731                 }\r
732                 return result;\r
733         }\r
734         \r
735         private static BinaryManager getBinaryManagerService() throws ClientException {\r
736                 BinaryManager result = null;\r
737                 try {\r
738                         result = Framework.getService(BinaryManager.class);\r
739                 } catch (Exception e) {\r
740                         String msg = "Unable to get Nuxeo's BinaryManager service.";\r
741                         logger.error(msg, e);\r
742                         throw new ClientException("msg", e);\r
743                 }\r
744                 return result;\r
745         }\r
746         \r
747 \r
748         /**\r
749          * Creates the picture.\r
750          * \r
751          * @param ctx\r
752          *            the ctx\r
753          * @param repoSession\r
754          *            the repo session\r
755          * @param filePath\r
756          *            the file path\r
757          * @return the string\r
758          * @throws Exception \r
759          */\r
760         public static BlobsCommon createBlobInRepository(ServiceContext ctx,\r
761                         RepositoryInstance repoSession,\r
762                         BlobInput blobInput,\r
763                         boolean purgeOriginal) throws Exception {\r
764                 BlobsCommon result = null;\r
765 \r
766                 try {\r
767                         File blobFile = blobInput.getBlobFile();\r
768                         String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();\r
769                         DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);\r
770                         DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);\r
771                         \r
772             if (logger.isTraceEnabled()) {\r
773                     for (String facet : wspaceDoc.getFacets()) {\r
774                         logger.trace("Facet: " + facet);\r
775                     }\r
776                     for (String docType : wspaceDoc.getDocumentType().getChildrenTypes()) {\r
777                         logger.trace("Child type: " + docType);\r
778                     }\r
779             }                   \r
780 \r
781                         result = createBlobInRepository(repoSession, wspaceDoc, purgeOriginal, blobFile, null /*mime type*/);\r
782                 } catch (Exception e) {\r
783                         logger.error("Could not create image blob", e);\r
784                         throw e;\r
785                 }\r
786 \r
787                 return result;\r
788         }\r
789         \r
790         /*\r
791          * Find out if this document's blob/file-contents are allowed to be purged.  For instance, we currently\r
792          * only want to allow the purging the contents of Nuxeo "Picture" documents. \r
793          */\r
794         static private boolean isPurgeAllowed(DocumentModel docModel) {\r
795                 boolean result = false;\r
796                 \r
797                 if (docModel.hasFacet(ImagingDocumentConstants.PICTURE_FACET) == true) {\r
798                         result = true; // Yes, delete/purge the original content\r
799                 }\r
800                 \r
801                 return result;\r
802         }\r
803 \r
804         /**\r
805          * Creates the image blob.\r
806          * \r
807          * @param nuxeoSession\r
808          *            the nuxeo session\r
809          * @param blobLocation\r
810          *            the blob location\r
811          * @param file\r
812          *            the file\r
813          * @param fileName\r
814          *            the file name\r
815          * @param mimeType\r
816          *            the mime type\r
817          * @return the string\r
818          */\r
819         static public BlobsCommon createBlobInRepository(RepositoryInstance nuxeoSession,\r
820                         DocumentModel blobLocation,\r
821                         boolean purgeOriginal,\r
822                         File file,\r
823                         String mimeType) {\r
824                 BlobsCommon result = null;\r
825 \r
826                 try {\r
827                         // Blob fileBlob = createStreamingBlob(blobFile, blobFile.getName(),\r
828                         // mimeType);\r
829                         Blob fileBlob = createNuxeoFileBasedBlob(file);\r
830                         String digestAlgorithm = getFileManagerService()\r
831                                         .getDigestAlgorithm(); // Only call this because we seem to need some way of initializing Nuxeo's FileManager with a call.\r
832                         \r
833                         logger.debug("Start --> Starting call to Nuxeo to create the blob document."); // For example, see Nuxeo's DefaultPictureAdapter class for details\r
834                         DocumentModel documentModel = getFileManagerService()\r
835                                         .createDocumentFromBlob(nuxeoSession, fileBlob,\r
836                                                         blobLocation.getPathAsString(), true,\r
837                                                         file.getName());\r
838                         logger.debug("Stop --> Finished calling Nuxeo to create the blob document.");\r
839 \r
840                         result = createBlobsCommon(documentModel, fileBlob); // Now create our metadata resource document\r
841 \r
842                         // If the sender wanted us to generate only derivatives, we need to purge/clear the original contents\r
843                         if (purgeOriginal == true && isPurgeAllowed(documentModel) == true) {\r
844                                 // Empty the document model's "content" property -this does not delete the actual file/blob\r
845                                 documentModel.setPropertyValue("file:content", (Serializable) null);\r
846                                 \r
847                                 if (documentModel.hasFacet(ImagingDocumentConstants.PICTURE_FACET)) {\r
848                                         // Now with no content, the derivative listener wants to update the derivatives. So to\r
849                                         // prevent the listener, we remove the "Picture" facet from the document\r
850                                         NuxeoUtils.removeFacet(documentModel, ImagingDocumentConstants.PICTURE_FACET); // Removing this facet ensures the original derivatives are unchanged.\r
851                                         nuxeoSession.saveDocument(documentModel);\r
852                                         // Now that we've emptied the document model's content field, we can add back the Picture facet\r
853                                         NuxeoUtils.addFacet(documentModel, ImagingDocumentConstants.PICTURE_FACET);\r
854                                 }\r
855                                 \r
856                                 nuxeoSession.saveDocument(documentModel);\r
857                                 // Next, we need to remove the actual file from Nuxeo's data directory\r
858                                 DocumentBlobHolder docBlobHolder = (DocumentBlobHolder) documentModel\r
859                                                 .getAdapter(BlobHolder.class);\r
860                                 boolean deleteSuccess = NuxeoUtils.deleteFileOfBlob(docBlobHolder.getBlob());\r
861                         }\r
862                 } catch (Exception e) {\r
863                         result = null;\r
864                         logger.error("Could not create new Nuxeo blob document.", e); //FIXME: REM - This should probably be re-throwing the exception?\r
865                 }\r
866 \r
867                 return result;\r
868         }\r
869 \r
870         // /*\r
871         // * This is an alternate approach to getting information about an image\r
872         // * and its corresponding derivatives.\r
873         // */\r
874         // // MultiviewPictureAdapter multiviewPictureAdapter =\r
875         // documentModel.getAdapter(MultiviewPictureAdapter.class);\r
876         // MultiviewPictureAdapterFactory multiviewPictureAdapterFactory = new\r
877         // MultiviewPictureAdapterFactory();\r
878         // MultiviewPictureAdapter multiviewPictureAdapter =\r
879         // (MultiviewPictureAdapter)multiviewPictureAdapterFactory.getAdapter(documentModel,\r
880         // null);\r
881         // if (multiviewPictureAdapter != null) {\r
882         // PictureView[] pictureViewArray = multiviewPictureAdapter.getViews();\r
883         // for (PictureView pictureView : pictureViewArray) {\r
884         // if (logger.isDebugEnabled() == true) {\r
885         // logger.debug("-------------------------------------");\r
886         // logger.debug(toStringPictureView(pictureView));\r
887         // }\r
888         // }\r
889         // }\r
890         \r
891         public static InputStream getResource(String resourceName) {\r
892                 InputStream result = null;\r
893                 \r
894                 try {\r
895                         result = ServiceMain.getInstance().getResourceAsStream(resourceName);\r
896                 } catch (FileNotFoundException e) {\r
897                         logger.error("Missing Services resource: " + resourceName, e);\r
898                 }\r
899         \r
900                 return result;\r
901         }\r
902 \r
903         /**\r
904          * Gets the image.\r
905          * \r
906          * @param repoSession\r
907          *            the repo session\r
908          * @param repositoryId\r
909          *            the repository id\r
910          * @param derivativeTerm\r
911          *            the derivative term\r
912          * @return the image\r
913          */\r
914         static public BlobOutput getBlobOutput(ServiceContext ctx,\r
915                         RepositoryInstance repoSession,\r
916                         String repositoryId,\r
917                         String derivativeTerm,\r
918                         Boolean getContentFlag,\r
919                         StringBuffer outMimeType) {\r
920                 BlobOutput result = new BlobOutput();\r
921                 boolean isNonImageDerivative = false;\r
922 \r
923                 if (repositoryId != null && repositoryId.isEmpty() == false)\r
924                         try {\r
925                                 IdRef documentRef = new IdRef(repositoryId);\r
926                                 DocumentModel documentModel = repoSession.getDocument(documentRef);\r
927 \r
928                                 Blob docBlob = null;\r
929                                 DocumentBlobHolder docBlobHolder = (DocumentBlobHolder) documentModel\r
930                                                 .getAdapter(BlobHolder.class);\r
931                                 if (docBlobHolder instanceof PictureBlobHolder) {\r
932                                         // if it is a PictureDocument then it has these\r
933                                         // Nuxeo schemas: [dublincore, uid, picture, iptc, common, image_metadata]\r
934                                         //\r
935                                         // Need to add the "MultiviewPictureAdapter" support here to\r
936                                         // get the view data, see above.\r
937                                         //\r
938                                         PictureBlobHolder pictureBlobHolder = (PictureBlobHolder) docBlobHolder;\r
939                                         if (derivativeTerm != null) {\r
940                                                 docBlob = pictureBlobHolder.getBlob(derivativeTerm);\r
941                                                 // Nuxeo derivatives are all JPEG\r
942                                                 outMimeType.append(MIME_JPEG); // All Nuxeo image derivatives are JPEG images.\r
943                                         } else {\r
944                                                 docBlob = pictureBlobHolder.getBlob();\r
945                                         }\r
946                                 } else {\r
947                                         docBlob = docBlobHolder.getBlob();\r
948                                         if (derivativeTerm != null) { // If its a derivative request on a non-image blob, then return just a document image thumnail\r
949                                                 isNonImageDerivative = true;\r
950                                         }\r
951                                 }\r
952 \r
953                                 //\r
954                                 // Create the result instance that will contain the blob metadata\r
955                                 // and an InputStream with the bits if the 'getContentFlag' is\r
956                                 // set.\r
957                                 //\r
958                                 BlobsCommon blobsCommon = createBlobsCommon(documentModel, docBlob);\r
959                                 result.setBlobsCommon(blobsCommon);\r
960                                 if (getContentFlag == true) {\r
961                                         InputStream remoteStream = null;\r
962                                         if (isNonImageDerivative == false) {\r
963                                                 remoteStream = docBlob.getStream(); // This will fail if the blob's file has been deleted. FileNotFoundException thrown.\r
964                                         } else {\r
965                                                 remoteStream = getResource(DOCUMENT_PLACEHOLDER_IMAGE);\r
966                                                 outMimeType.append(MIME_JPEG);\r
967                                         }\r
968                                         BufferedInputStream bufferedInputStream = new BufferedInputStream(\r
969                                                         remoteStream);  \r
970                                         result.setBlobInputStream(bufferedInputStream);\r
971                                 }\r
972                         } catch (Exception e) {\r
973                                 if (logger.isErrorEnabled() == true) {\r
974                                         logger.error(e.getMessage(), e);\r
975                                 }\r
976                                 result = null;\r
977                         }\r
978 \r
979                 return result;\r
980         }\r
981         \r
982 }\r
983 \r
984 /*\r
985  * Notes and code snippets about Nuxeo's support for binaries and image\r
986  * documents.\r
987  */\r
988 \r
989 /*\r
990  * \r
991  * \r
992  * MultiviewPictureAdapter org.nuxeo.ecm.platform.picture.api.adapters\r
993  * PictureResourceAdapter pictureResourceAdapter = (PictureResourceAdapter)\r
994  * documentModel.getAdapter(PictureResourceAdapter.class); String thumbnailPath\r
995  * = pictureResourceAdapter.getViewXPath("Thumbnail");\r
996  * \r
997  * Map<String,Serializable> blobHolderProps = docBlobHolder.getProperties();\r
998  * String filePath = docBlobHolder.getFilePath(); List<Blob> docBlobs =\r
999  * docBlobHolder.getBlobs();\r
1000  * \r
1001  * stream = new FileInputStream(fileUploadHolder.getTempFile());\r
1002  * \r
1003  * public String addFile(InputStream fileUpload, String fileName) fileName =\r
1004  * FileUtils.getCleanFileName(fileName); DocumentModel currentDocument =\r
1005  * navigationContext.getCurrentDocument(); String path =\r
1006  * currentDocument.getPathAsString(); Blob blob =\r
1007  * FileUtils.createSerializableBlob(fileUpload, fileName, null);\r
1008  * \r
1009  * DocumentModel createdDoc = getFileManagerService().createDocumentFromBlob(\r
1010  * documentManager, blob, path, true, fileName);\r
1011  * eventManager.raiseEventsOnDocumentSelected(createdDoc);\r
1012  * \r
1013  * protected FileManager fileManager;\r
1014  * \r
1015  * protected FileManager getFileManagerService() throws ClientException { if\r
1016  * (fileManager == null) { try { fileManager =\r
1017  * Framework.getService(FileManager.class); } catch (Exception e) {\r
1018  * log.error("Unable to get FileManager service ", e); throw new\r
1019  * ClientException("Unable to get FileManager service ", e); } } return\r
1020  * fileManager; }\r
1021  */\r
1022 \r
1023 /*\r
1024  * RepositoryService repositoryService = (RepositoryService)\r
1025  * Framework.getRuntime().getComponent( RepositoryService.NAME);\r
1026  * RepositoryManager repositoryManager =\r
1027  * repositoryService.getRepositoryManager(); RepositoryDescriptor descriptor =\r
1028  * repositoryManager.getDescriptor(repositoryName); DefaultBinaryManager\r
1029  * binaryManager = new DefaultBinaryManager(\r
1030  * SQLRepository.getDescriptor(descriptor)));\r
1031  * \r
1032  * File storageDir = binaryManager.getStorageDir(); SQLBlob blob = (SQLBlob)\r
1033  * doc.getPropertyValue("schema:blobField"); File file =\r
1034  * binaryManager.getFileForDigest( blob.getBinary().getDigest(), false);\r
1035  */\r
1036 \r
1037 /*\r
1038  * RepositoryInstance.getStreamURI()\r
1039  * \r
1040  * String getStreamURI(String blobPropertyId) throws ClientException\r
1041  * \r
1042  * Returns an URI identifying the stream given the blob property id. This method\r
1043  * should be used by a client to download the data of a blob property.\r
1044  * \r
1045  * The blob is fetched from the repository and the blob stream is registered\r
1046  * against the streaming service so the stream will be available remotely\r
1047  * through stream service API.\r
1048  * \r
1049  * After the client has called this method, it will be able to download the\r
1050  * stream using streaming server API.\r
1051  * \r
1052  * Returns: an URI identifying the remote stream Throws: ClientException\r
1053  */\r
1054 \r
1055 /*\r
1056  * A blob contains usually large data.\r
1057  * \r
1058  * Document fields holding Blob data are by default fetched in a lazy manner.\r
1059  * \r
1060  * A Blob object hides the data source and it also describes data properties\r
1061  * like the encoding or mime-type.\r
1062  * \r
1063  * The encoding is used to decode Unicode text content that was stored in an\r
1064  * encoded form. If not encoding is specified, the default java encoding is\r
1065  * used. The encoding is ignored for binary content.\r
1066  * \r
1067  * When retrieving the content from a document, it will be returned as source\r
1068  * content instead of returning the content bytes.\r
1069  * \r
1070  * The same is true when setting the content for a document: you set a content\r
1071  * source and not directly the content bytes. Ex:\r
1072  * \r
1073  * File file = new File("/tmp/index.html"); FileBlob fb = new FileBlob(file);\r
1074  * fb.setMimeType("text/html"); fb.setEncoding("UTF-8"); // this specifies that\r
1075  * content bytes will be stored as UTF-8 document.setProperty("file", "content",\r
1076  * fb);\r
1077  * \r
1078  * \r
1079  * Then you may want to retrieve the content as follow:\r
1080  * \r
1081  * Blob blob = document.getProperty("file:content"); htmlDoc = blob.getString();\r
1082  * // the content is decoded from UTF-8 into a java string\r
1083  */\r