/**
  * Convenience method to check the transformability of a transformation
  *
  * @param reader content reader
  * @param writer content writer
  * @param options transformation options
  * @throws AlfrescoRuntimeException if the the transformation isn't supported
  */
 protected void checkTransformable(
     ContentReader reader, ContentWriter writer, TransformationOptions options) {
   String sourceMimetype = getMimetype(reader);
   String targetMimetype = getMimetype(writer);
   long sourceSize = reader.getSize();
   boolean transformable = isTransformable(sourceMimetype, sourceSize, targetMimetype, options);
   if (transformable == false) {
     // This method is only called once a transformer has been selected, so it should be able to
     // handle the mimetypes but might not be able to handle all the limits as it might be part of
     // of a complex (compound) transformer. So report the max size if set.
     long maxSourceSizeKBytes = getMaxSourceSizeKBytes(sourceMimetype, targetMimetype, options);
     boolean sizeOkay =
         maxSourceSizeKBytes < 0
             || (maxSourceSizeKBytes > 0 && sourceSize <= maxSourceSizeKBytes * 1024);
     AlfrescoRuntimeException e =
         new UnsupportedTransformationException(
             "Unsupported transformation: "
                 + getBeanName()
                 + ' '
                 + sourceMimetype
                 + " to "
                 + targetMimetype
                 + ' '
                 + (sizeOkay
                     ? ""
                     : transformerDebug.fileSize(sourceSize)
                         + " > "
                         + transformerDebug.fileSize(maxSourceSizeKBytes * 1024)));
     throw transformerDebug.setCause(e);
   }
   // it all checks out OK
 }
  @Test
  public void putForZeroLengthFile() {
    ContentReader contentReader = Mockito.mock(ContentReader.class);
    Mockito.when(contentReader.getSize()).thenReturn(0L);

    boolean putResult = contentCache.put("", contentReader);

    assertFalse("Zero length files should not be cached", putResult);
  }
Example #3
0
  @Override
  public boolean put(String contentUrl, ContentReader source) {
    File cacheFile = createCacheFile();

    // Copy the content from the source into a cache file
    if (source.getSize() > 0L) {
      source.getContent(cacheFile);
      // Add a record of the cached file to the in-memory cache.
      recordCacheEntries(contentUrl, cacheFile);
      return true;
    }

    return false;
  }
  @Test
  public void putForNonEmptyFile() {
    ContentReader contentReader = Mockito.mock(ContentReader.class);
    Mockito.when(contentReader.getSize()).thenReturn(999000L);
    final String url = "store://some/url.bin";
    boolean putResult = contentCache.put(url, contentReader);

    assertTrue("Non-empty files should be cached", putResult);
    ArgumentCaptor<File> cacheFileArg = ArgumentCaptor.forClass(File.class);
    Mockito.verify(contentReader).getContent(cacheFileArg.capture());
    // Check cached item is recorded properly in ehcache
    Mockito.verify(lookupTable).put(Key.forUrl(url), cacheFileArg.getValue().getAbsolutePath());
    Mockito.verify(lookupTable)
        .put(Key.forCacheFile(cacheFileArg.getValue().getAbsolutePath()), url);
  }
  /**
   * Stream content implementation
   *
   * @param req The request
   * @param res The response
   * @param reader The reader
   * @param nodeRef The content nodeRef if applicable
   * @param propertyQName The content property if applicable
   * @param attach Indicates whether the content should be streamed as an attachment or not
   * @param modified Modified date of content
   * @param eTag ETag to use
   * @param attachFileName Optional file name to use when attach is <code>true</code>
   * @throws IOException
   */
  public void streamContentImpl(
      WebScriptRequest req,
      WebScriptResponse res,
      ContentReader reader,
      final NodeRef nodeRef,
      final QName propertyQName,
      final boolean attach,
      final Date modified,
      String eTag,
      final String attachFileName,
      Map<String, Object> model)
      throws IOException {
    setAttachment(null, res, attach, attachFileName);

    // establish mimetype
    String mimetype = reader.getMimetype();
    String extensionPath = req.getExtensionPath();
    if (mimetype == null || mimetype.length() == 0) {
      mimetype = MimetypeMap.MIMETYPE_BINARY;
      int extIndex = extensionPath.lastIndexOf('.');
      if (extIndex != -1) {
        String ext = extensionPath.substring(extIndex + 1);
        mimetype = mimetypeService.getMimetype(ext);
      }
    }

    res.setHeader(HEADER_ACCEPT_RANGES, "bytes");
    try {
      boolean processedRange = false;
      String range = req.getHeader(HEADER_CONTENT_RANGE);
      final long size = reader.getSize();
      final String encoding = reader.getEncoding();

      if (attach) {
        final String finalMimetype = mimetype;

        eventPublisher.publishEvent(
            new EventPreparator() {
              @Override
              public Event prepareEvent(String user, String networkId, String transactionId) {
                String siteId = siteService.getSiteShortName(nodeRef);

                return new ContentEventImpl(
                    ContentEvent.DOWNLOAD,
                    user,
                    networkId,
                    transactionId,
                    nodeRef.getId(),
                    siteId,
                    propertyQName.toString(),
                    Client.webclient,
                    attachFileName,
                    finalMimetype,
                    size,
                    encoding);
              }
            });
      }

      if (range == null) {
        range = req.getHeader(HEADER_RANGE);
      }
      if (range != null) {
        if (logger.isDebugEnabled()) logger.debug("Found content range header: " + range);

        // ensure the range header is starts with "bytes=" and process the range(s)
        if (range.length() > 6) {
          if (range.indexOf(',') != -1 && (nodeRef == null || propertyQName == null)) {
            if (logger.isInfoEnabled()) logger.info("Multi-range only supported for nodeRefs");
          } else {
            HttpRangeProcessor rangeProcessor = new HttpRangeProcessor(contentService);
            processedRange =
                rangeProcessor.processRange(
                    res,
                    reader,
                    range.substring(6),
                    nodeRef,
                    propertyQName,
                    mimetype,
                    req.getHeader(HEADER_USER_AGENT));
          }
        }
      }
      if (processedRange == false) {
        if (logger.isDebugEnabled()) logger.debug("Sending complete file content...");

        // set mimetype for the content and the character encoding for the stream
        res.setContentType(mimetype);
        res.setContentEncoding(encoding);

        // return the complete entity range
        res.setHeader(
            HEADER_CONTENT_RANGE,
            "bytes 0-" + Long.toString(size - 1L) + "/" + Long.toString(size));
        res.setHeader(HEADER_CONTENT_LENGTH, Long.toString(size));

        // set caching
        setResponseCache(res, modified, eTag, model);

        // get the content and stream directly to the response output stream
        // assuming the repository is capable of streaming in chunks, this should allow large files
        // to be streamed directly to the browser response stream.
        reader.getContent(res.getOutputStream());
      }
    } catch (SocketException e1) {
      // the client cut the connection - our mission was accomplished apart from a little error
      // message
      if (logger.isInfoEnabled()) logger.info("Client aborted stream read:\n\tcontent: " + reader);
    } catch (ContentIOException e2) {
      if (logger.isInfoEnabled()) logger.info("Client aborted stream read:\n\tcontent: " + reader);
    }
  }
  /**
   * @see
   *     org.alfresco.repo.content.transform.ContentTransformer#transform(org.alfresco.service.cmr.repository.ContentReader,
   *     org.alfresco.service.cmr.repository.ContentWriter,
   *     org.alfresco.service.cmr.repository.TransformationOptions)
   */
  public final void transform(
      ContentReader reader, ContentWriter writer, TransformationOptions options)
      throws ContentIOException {
    try {
      depth.set(depth.get() + 1);

      // begin timing
      long before = System.currentTimeMillis();

      String sourceMimetype = reader.getMimetype();
      String targetMimetype = writer.getMimetype();

      // check options map
      if (options == null) {
        options = new TransformationOptions();
      }

      try {
        if (transformerDebug.isEnabled()) {
          transformerDebug.pushTransform(
              this,
              reader.getContentUrl(),
              sourceMimetype,
              targetMimetype,
              reader.getSize(),
              options);
        }

        // Check the transformability
        checkTransformable(reader, writer, options);

        // Pass on any limits to the reader
        setReaderLimits(reader, writer, options);

        // Transform
        // MNT-12238: CLONE - CLONE - Upload of PPTX causes very high memory usage leading to system
        // instability
        // Limiting transformation up to configured amount of milliseconds to avoid very high RAM
        // consumption
        // and OOM during transforming problematic documents
        TransformationOptionLimits limits =
            getLimits(reader.getMimetype(), writer.getMimetype(), options);

        long timeoutMs = limits.getTimeoutMs();
        if (!useTimeoutThread || (null == limits) || (-1 == timeoutMs)) {
          transformInternal(reader, writer, options);
        } else {
          Future<?> submittedTask = null;
          StreamAwareContentReaderProxy proxiedReader = new StreamAwareContentReaderProxy(reader);
          StreamAwareContentWriterProxy proxiedWriter = new StreamAwareContentWriterProxy(writer);

          try {
            submittedTask =
                getExecutorService()
                    .submit(new TransformInternalCallable(proxiedReader, proxiedWriter, options));
            submittedTask.get(timeoutMs + additionalThreadTimout, TimeUnit.MILLISECONDS);
          } catch (TimeoutException e) {
            releaseResources(submittedTask, proxiedReader, proxiedWriter);
            throw new TimeoutException("Transformation failed due to timeout limit");
          } catch (InterruptedException e) {
            releaseResources(submittedTask, proxiedReader, proxiedWriter);
            throw new InterruptedException(
                "Transformation failed, because the thread of the transformation was interrupted");
          } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof TransformInternalCallableException) {
              cause = ((TransformInternalCallableException) cause).getCause();
            }

            throw cause;
          }
        }

        // record time
        long after = System.currentTimeMillis();
        recordTime(sourceMimetype, targetMimetype, after - before);
      } catch (ContentServiceTransientException cste) {
        // A transient failure has occurred within the content transformer.
        // This should not be interpreted as a failure and therefore we should not
        // update the transformer's average time.
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Transformation has been transiently declined: \n"
                  + "   reader: "
                  + reader
                  + "\n"
                  + "   writer: "
                  + writer
                  + "\n"
                  + "   options: "
                  + options
                  + "\n"
                  + "   transformer: "
                  + this);
        }
        // the finally block below will still perform tidyup. Otherwise we're done.
        // We rethrow the exception
        throw cste;
      } catch (UnsupportedTransformationException e) {
        // Don't record an error or even the time, as this is normal in compound transformations.
        transformerDebug.debug("          Failed", e);
        throw e;
      } catch (Throwable e) {
        // Make sure that this transformation gets set back i.t.o. time taken.
        // This will ensure that transformers that compete for the same transformation
        // will be prejudiced against transformers that tend to fail
        long after = System.currentTimeMillis();
        recordError(sourceMimetype, targetMimetype, after - before);

        // Ask Tika to detect the document, and report back on if
        //  the current mime type is plausible
        String differentType = getMimetypeService().getMimetypeIfNotMatches(reader.getReader());

        // Report the error
        if (differentType == null) {
          transformerDebug.debug("          Failed", e);
          throw new ContentIOException(
              "Content conversion failed: \n"
                  + "   reader: "
                  + reader
                  + "\n"
                  + "   writer: "
                  + writer
                  + "\n"
                  + "   options: "
                  + options.toString(false)
                  + "\n"
                  + "   limits: "
                  + getLimits(reader, writer, options),
              e);
        } else {
          transformerDebug.debug("          Failed: Mime type was '" + differentType + "'", e);

          if (this.retryTransformOnDifferentMimeType) {
            // MNT-11015 fix.
            // Set a new reader to refresh the input stream.
            reader = reader.getReader();
            // set the actual file MIME type detected by Tika for content reader
            reader.setMimetype(differentType);

            // Get correct transformer according actual file MIME type and try to transform file
            // with
            // actual transformer
            ContentTransformer transformer =
                this.registry.getTransformer(
                    differentType, reader.getSize(), targetMimetype, options);
            if (null != transformer) {
              transformer.transform(reader, writer, options);
            } else {
              transformerDebug.debug("          Failed", e);
              throw new ContentIOException(
                  "Content conversion failed: \n"
                      + "   reader: "
                      + reader
                      + "\n"
                      + "   writer: "
                      + writer
                      + "\n"
                      + "   options: "
                      + options.toString(false)
                      + "\n"
                      + "   limits: "
                      + getLimits(reader, writer, options)
                      + "\n"
                      + "   claimed mime type: "
                      + reader.getMimetype()
                      + "\n"
                      + "   detected mime type: "
                      + differentType
                      + "\n"
                      + "   transformer not found"
                      + "\n",
                  e);
            }
          } else {
            throw new ContentIOException(
                "Content conversion failed: \n"
                    + "   reader: "
                    + reader
                    + "\n"
                    + "   writer: "
                    + writer
                    + "\n"
                    + "   options: "
                    + options.toString(false)
                    + "\n"
                    + "   limits: "
                    + getLimits(reader, writer, options)
                    + "\n"
                    + "   claimed mime type: "
                    + reader.getMimetype()
                    + "\n"
                    + "   detected mime type: "
                    + differentType,
                e);
          }
        }
      } finally {
        transformerDebug.popTransform();

        // check that the reader and writer are both closed
        if (reader.isChannelOpen()) {
          logger.error(
              "Content reader not closed by transformer: \n"
                  + "   reader: "
                  + reader
                  + "\n"
                  + "   transformer: "
                  + this);
        }
        if (writer.isChannelOpen()) {
          logger.error(
              "Content writer not closed by transformer: \n"
                  + "   writer: "
                  + writer
                  + "\n"
                  + "   transformer: "
                  + this);
        }
      }

      // done
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Completed transformation: \n"
                + "   reader: "
                + reader
                + "\n"
                + "   writer: "
                + writer
                + "\n"
                + "   options: "
                + options
                + "\n"
                + "   transformer: "
                + this);
      }
    } finally {
      depth.set(depth.get() - 1);
    }
  }
  /**
   * @see
   *     org.alfresco.repo.content.transform.AbstractContentTransformer2#transformInternal(org.alfresco.service.cmr.repository.ContentReader,
   *     org.alfresco.service.cmr.repository.ContentWriter,
   *     org.alfresco.service.cmr.repository.TransformationOptions)
   */
  @Override
  public void transformInternal(
      ContentReader reader, ContentWriter writer, TransformationOptions options) throws Exception {
    final String outputMimetype = writer.getMimetype();
    final String outputFileExt = getMimetypeService().getExtension(outputMimetype);

    // We need to keep a reference to thrown exceptions as we're going to catch them and
    // then move on to the next transformer. In the event that they all fail, we will throw
    // the final exception.
    Exception transformationException = null;

    for (int i = 0; i < transformers.size(); i++) {
      int oneBasedCount = i + 1;
      ContentTransformer transf = transformers.get(i);
      ContentWriter currentWriter = null;
      File tempFile = null;
      try {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Transformation attempt "
                  + oneBasedCount
                  + " of "
                  + transformers.size()
                  + ": "
                  + transf);
        }

        if (!transf.isTransformable(
            reader.getMimetype(), reader.getSize(), outputMimetype, options)) {
          throw new UnsupportedTransformationException(
              "Unsupported transformation: " + reader.getMimetype() + " to " + outputMimetype);
        }

        // We can't know in advance which transformer in the sequence will work - if any.
        // Therefore we can't write into the ContentWriter stream.
        // So make a temporary file writer with the current transformer name.
        tempFile =
            TempFileProvider.createTempFile(
                "FailoverTransformer_intermediate_" + transf.getClass().getSimpleName() + "_",
                "." + outputFileExt);
        currentWriter = new FileContentWriter(tempFile);
        currentWriter.setMimetype(outputMimetype);
        currentWriter.setEncoding(writer.getEncoding());

        // attempt to transform
        transf.transform(reader, currentWriter, options);

        // TODO Could add a check for zero-length output and treat that as a failure
        // final long writtenSize = currentWriter.getSize();
      } catch (Exception are) {
        if (transformationException == null) {
          transformationException = are;
        }

        if (logger.isDebugEnabled()) {
          logger.debug("Transformation " + oneBasedCount + " was unsuccessful.");
          if (i != transformers.size() - 1) {
            // We don't log the last exception as we're going to throw it.
            logger.debug("The below exception is provided for information purposes only.", are);
          }
        }

        // Set a new reader to refresh the input stream.
        reader = reader.getReader();
        // and move to the next transformer
        continue;
      }
      // No need to close input or output streams

      // At this point the current transformation was successful i.e. it did not throw an exception.

      // Now we must copy the content from the temporary file into the ContentWriter stream.
      if (tempFile != null) {
        writer.putContent(tempFile);
      }

      if (logger.isInfoEnabled()) {
        logger.info("Transformation was successful");
      }
      return;
    }
    // At this point we have tried all transformers in the sequence without apparent success.
    if (transformationException != null) {
      transformerDebug.debug("          No more transformations to failover to");
      if (logger.isDebugEnabled()) {
        logger.debug(
            "All transformations were unsuccessful. Throwing first exception.",
            transformationException);
      }
      throw transformationException;
    }
  }
Example #8
0
  private AlfrescoRuntimeException signFile(
      final NodeRef nodeRefToSign,
      final DigitalSigningDTO signingDTO,
      final File alfTempDir,
      final String alias,
      final KeyStore ks,
      final PrivateKey key,
      final Certificate[] chain) {
    final String fileNameToSign = fileFolderService.getFileInfo(nodeRefToSign).getName();

    File fileConverted = null;
    File tempDir = null;
    try {
      ContentReader fileToSignContentReader = getReader(nodeRefToSign);

      if (fileToSignContentReader != null) {
        String newName = null;

        // Check if document is PDF or transform it
        if (!MimetypeMap.MIMETYPE_PDF.equals(fileToSignContentReader.getMimetype())) {
          // Transform document in PDF document
          final ContentTransformer tranformer =
              contentTransformerRegistry.getTransformer(
                  fileToSignContentReader.getMimetype(),
                  fileToSignContentReader.getSize(),
                  MimetypeMap.MIMETYPE_PDF,
                  new TransformationOptions());

          if (tranformer != null) {

            tempDir = new File(alfTempDir.getPath() + File.separatorChar + nodeRefToSign.getId());
            if (tempDir != null) {
              tempDir.mkdir();
              fileConverted =
                  new File(tempDir, fileNameToSign + "_" + System.currentTimeMillis() + ".pdf");
              if (fileConverted != null) {
                final ContentWriter newDoc = new FileContentWriter(fileConverted);
                if (newDoc != null) {
                  newDoc.setMimetype(MimetypeMap.MIMETYPE_PDF);
                  tranformer.transform(fileToSignContentReader, newDoc);
                  fileToSignContentReader = new FileContentReader(fileConverted);

                  final String originalName =
                      (String) nodeService.getProperty(nodeRefToSign, ContentModel.PROP_NAME);

                  newName = originalName.substring(0, originalName.lastIndexOf(".")) + ".pdf";
                }
              }
            }
          } else {
            log.error(
                "["
                    + fileNameToSign
                    + "] No suitable converter found to convert the document in PDF.");
            return new AlfrescoRuntimeException(
                "["
                    + fileNameToSign
                    + "] No suitable converter found to convert the document in PDF.");
          }
        }

        // Convert PDF in PDF/A format
        final File pdfAFile = convertPdfToPdfA(fileToSignContentReader.getContentInputStream());

        final PdfReader reader = new PdfReader(new FileInputStream(pdfAFile));

        if (nodeRefToSign != null) {
          tempDir = new File(alfTempDir.getPath() + File.separatorChar + nodeRefToSign.getId());
          if (tempDir != null) {
            tempDir.mkdir();
            final File file = new File(tempDir, fileNameToSign);

            if (file != null) {
              final FileOutputStream fout = new FileOutputStream(file);
              final PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');

              if (stp != null) {
                final PdfSignatureAppearance sap = stp.getSignatureAppearance();
                if (sap != null) {
                  sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
                  sap.setReason(signingDTO.getSignReason());
                  sap.setLocation(signingDTO.getSignLocation());
                  sap.setContact(signingDTO.getSignContact());
                  sap.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
                  sap.setImageScale(1);

                  // digital signature
                  if (signingDTO.getSigningField() != null
                      && !signingDTO.getSigningField().trim().equalsIgnoreCase("")) {
                    Image img = null;
                    if (signingDTO.getImage() != null) {
                      final ContentReader imageContentReader = getReader(signingDTO.getImage());
                      final AcroFields af = reader.getAcroFields();
                      if (af != null) {
                        final List<FieldPosition> positions =
                            af.getFieldPositions(signingDTO.getSigningField());
                        if (positions != null
                            && positions.size() > 0
                            && positions.get(0) != null
                            && positions.get(0).position != null) {
                          final BufferedImage newImg =
                              scaleImage(
                                  ImageIO.read(imageContentReader.getContentInputStream()),
                                  BufferedImage.TYPE_INT_RGB,
                                  Float.valueOf(positions.get(0).position.getWidth()).intValue(),
                                  Float.valueOf(positions.get(0).position.getHeight()).intValue());
                          img = Image.getInstance(newImg, null);
                        } else {
                          log.error(
                              "["
                                  + fileNameToSign
                                  + "] The field '"
                                  + signingDTO.getSigningField()
                                  + "' doesn't exist in the document.");
                          return new AlfrescoRuntimeException(
                              "["
                                  + fileNameToSign
                                  + "] The field '"
                                  + signingDTO.getSigningField()
                                  + "' doesn't exist in the document.");
                        }
                      }
                      if (img == null) {
                        img =
                            Image.getInstance(
                                ImageIO.read(imageContentReader.getContentInputStream()), null);
                      }
                      sap.setImage(img);
                    }
                    sap.setVisibleSignature(signingDTO.getSigningField());
                  } else {
                    int pageToSign = 1;
                    if (DigitalSigningDTO.PAGE_LAST.equalsIgnoreCase(
                        signingDTO.getPages().trim())) {
                      pageToSign = reader.getNumberOfPages();
                    } else if (DigitalSigningDTO.PAGE_SPECIFIC.equalsIgnoreCase(
                        signingDTO.getPages().trim())) {
                      if (signingDTO.getPageNumber() > 0
                          && signingDTO.getPageNumber() <= reader.getNumberOfPages()) {
                        pageToSign = signingDTO.getPageNumber();
                      } else {
                        throw new AlfrescoRuntimeException("Page number is out of bound.");
                      }
                    }
                    if (signingDTO.getImage() != null) {
                      final ContentReader imageContentReader = getReader(signingDTO.getImage());
                      // Resize image
                      final BufferedImage newImg =
                          scaleImage(
                              ImageIO.read(imageContentReader.getContentInputStream()),
                              BufferedImage.TYPE_INT_RGB,
                              signingDTO.getSignWidth(),
                              signingDTO.getSignHeight());
                      final Image img = Image.getInstance(newImg, null);
                      sap.setImage(img);
                    }
                    if (signingDTO.getPosition() != null
                        && !DigitalSigningDTO.POSITION_CUSTOM.equalsIgnoreCase(
                            signingDTO.getPosition().trim())) {
                      final Rectangle pageRect = reader.getPageSizeWithRotation(1);
                      sap.setVisibleSignature(
                          positionSignature(
                              signingDTO.getPosition(),
                              pageRect,
                              signingDTO.getSignWidth(),
                              signingDTO.getSignHeight(),
                              signingDTO.getxMargin(),
                              signingDTO.getyMargin()),
                          pageToSign,
                          null);
                    } else {
                      sap.setVisibleSignature(
                          new Rectangle(
                              signingDTO.getLocationX(),
                              signingDTO.getLocationY(),
                              signingDTO.getLocationX() + signingDTO.getSignWidth(),
                              signingDTO.getLocationY() - signingDTO.getSignHeight()),
                          pageToSign,
                          null);
                    }
                  }
                  stp.close();

                  NodeRef destinationNode = null;
                  NodeRef originalDoc = null;
                  boolean addAsNewVersion = false;
                  if (signingDTO.getDestinationFolder() == null) {
                    destinationNode = nodeRefToSign;
                    nodeService.addAspect(destinationNode, ContentModel.ASPECT_VERSIONABLE, null);
                    addAsNewVersion = true;
                  } else {
                    originalDoc = nodeRefToSign;
                    destinationNode =
                        createDestinationNode(
                            file.getName(), signingDTO.getDestinationFolder(), nodeRefToSign);
                  }

                  if (destinationNode != null) {

                    final ContentWriter writer =
                        contentService.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
                    if (writer != null) {
                      writer.setEncoding(fileToSignContentReader.getEncoding());
                      writer.setMimetype("application/pdf");
                      writer.putContent(file);
                      file.delete();

                      if (fileConverted != null) {
                        fileConverted.delete();
                      }

                      nodeService.addAspect(
                          destinationNode,
                          SigningModel.ASPECT_SIGNED,
                          new HashMap<QName, Serializable>());
                      nodeService.setProperty(
                          destinationNode, SigningModel.PROP_REASON, signingDTO.getSignReason());
                      nodeService.setProperty(
                          destinationNode,
                          SigningModel.PROP_LOCATION,
                          signingDTO.getSignLocation());
                      nodeService.setProperty(
                          destinationNode, SigningModel.PROP_SIGNATUREDATE, new java.util.Date());
                      nodeService.setProperty(
                          destinationNode,
                          SigningModel.PROP_SIGNEDBY,
                          AuthenticationUtil.getRunAsUser());

                      if (newName != null) {
                        nodeService.setProperty(destinationNode, ContentModel.PROP_NAME, newName);
                      }

                      final X509Certificate c = (X509Certificate) ks.getCertificate(alias);
                      nodeService.setProperty(
                          destinationNode, SigningModel.PROP_VALIDITY, c.getNotAfter());
                      nodeService.setProperty(
                          destinationNode, SigningModel.PROP_ORIGINAL_DOC, originalDoc);

                      if (!addAsNewVersion) {
                        if (!nodeService.hasAspect(originalDoc, SigningModel.ASPECT_ORIGINAL_DOC)) {
                          nodeService.addAspect(
                              originalDoc,
                              SigningModel.ASPECT_ORIGINAL_DOC,
                              new HashMap<QName, Serializable>());
                        }
                        nodeService.createAssociation(
                            originalDoc, destinationNode, SigningModel.PROP_RELATED_DOC);
                      }
                    }
                  } else {
                    log.error("[" + fileNameToSign + "] Destination node is not a valid NodeRef.");
                    return new AlfrescoRuntimeException(
                        "[" + fileNameToSign + "] Destination node is not a valid NodeRef.");
                  }
                } else {
                  log.error("[" + fileNameToSign + "] Unable to get PDF appearance signature.");
                  return new AlfrescoRuntimeException(
                      "[" + fileNameToSign + "] Unable to get PDF appearance signature.");
                }
              } else {
                log.error("[" + fileNameToSign + "] Unable to create PDF signature.");
                return new AlfrescoRuntimeException(
                    "[" + fileNameToSign + "] Unable to create PDF signature.");
              }
            }
          }
        } else {
          log.error("[" + fileNameToSign + "] Unable to get document to sign content.");
          return new AlfrescoRuntimeException(
              "[" + fileNameToSign + "] Unable to get document to sign content.");
        }

        if (pdfAFile != null) {
          pdfAFile.delete();
        }

        return null;

      } else {
        log.error("[" + fileNameToSign + "] The document has no content.");
        return new AlfrescoRuntimeException(
            "[" + fileNameToSign + "] The document has no content.");
      }
    } catch (KeyStoreException e) {
      log.error("[" + fileNameToSign + "] " + e);
      return new AlfrescoRuntimeException("[" + fileNameToSign + "] " + e.getMessage(), e);
    } catch (ContentIOException e) {
      log.error("[" + fileNameToSign + "] " + e);
      return new AlfrescoRuntimeException("[" + fileNameToSign + "] " + e.getMessage(), e);
    } catch (IOException e) {
      log.error("[" + fileNameToSign + "] " + e);
      return new AlfrescoRuntimeException("[" + fileNameToSign + "] " + e.getMessage(), e);
    } catch (DocumentException e) {
      log.error("[" + fileNameToSign + "] " + e);
      return new AlfrescoRuntimeException("[" + fileNameToSign + "] " + e.getMessage(), e);
    } finally {
      if (tempDir != null) {
        try {
          tempDir.delete();
        } catch (Exception ex) {
          log.error("[" + fileNameToSign + "] " + ex);
          return new AlfrescoRuntimeException("[" + fileNameToSign + "] " + ex.getMessage(), ex);
        }
      }
    }
  }