@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    @Caching(
        evict = {
          @CacheEvict(value = "product", allEntries = true),
          @CacheEvict(value = "products", allEntries = true)
        })
    @IncrementCache(name = "product_count", key = "all", value = 1)
    public void run() {
      if (scanner != null && scanner.isStopped()) {
        logger.info("Scanner stopped, deleting product #" + product.getId());
        if (wrapper != null) {
          wrapper.error(product, new InterruptedException("Processing stopped by the user"));
        }
        productDao.delete(product);
        return;
      }
      logger.debug("Add product \"" + ProductDao.getPathFromProduct(product) + "\".");

      try {
        long processing_start = System.currentTimeMillis();

        if (wrapper != null) {
          wrapper.startIngestion();
        }

        processingManager.process(product);
        productDao.update(product);
        searchService.index(product);

        if (collections != null) {
          for (Collection c : collections) {
            collectionService.systemAddProduct(c.getId(), product.getId(), true);
          }
        }

        if (wrapper != null) {
          wrapper.endIngestion();
        }

        long processing_end = System.currentTimeMillis();
        logger.info(
            "Ingestion processing complete for product "
                + product.getPath().toExternalForm()
                + " ("
                + product.getSize()
                + " bytes, "
                + product.getDownloadableSize()
                + " bytes compressed)"
                + " in "
                + (processing_end - processing_start)
                + "ms.");

        actionRecordWritterDao.uploadEnd(
            this.product.getPath(), this.owner.getUsername(), collections, true);
      } catch (Exception excp) {
        logger.warn(
            "Unrecoverable error happen during ingestion of "
                + product.getPath()
                + " (removing from database)",
            excp);
        try {
          productDao.delete(product);
        } catch (Exception e) {
          logger.error("Unable to remove product after ingestion failure", e);
        }
        if (wrapper != null) {
          wrapper.error(product, excp);
        }

        if ((this.product.getPath() != null) && (this.owner != null)) {
          actionRecordWritterDao.uploadFailed(
              this.product.getPath().toString(), this.owner.getUsername());
          actionRecordWritterDao.uploadEnd(
              this.product.getPath(), this.owner.getUsername(), collections, false);
        }
        return;
      }
    }