3

I have a Java-app with an InputStream, which is copying data to an OutputStream. I want to compress the data from the InputStream using FreeArc, before writing it to the OutputStream.

The problem is that there's no Java-API against FreeArc. I therefore need to pipe it through the command-line exe somehow. I.e. I have to fool FreeArc that it's reading and writing two files when it's in fact reading from my InputStream and writing to my OutputStream. On Unix this is quite straightforward but I have to make this work on Windows.

How do you suggest I do that? Is there a way to access Windows' Named Pipes in Java or can I do it over sockets? Something else? This will be done ~1/sec so the overhead can't be too high.

Yrlec
  • 3,401
  • 6
  • 39
  • 75

3 Answers3

2

If freearc can take input from standard in and send output to standard out, then ProcessBuilder can be used to invoke the command and the Process instance returned will expose the external process' standard input and output.

biziclop
  • 48,926
  • 12
  • 77
  • 104
1

Here is an example taken from code meant to convert EPS to PDF via GhostScript:

  // Start the script as OS process.
  ProcessBuilder pb = new ProcessBuilder(gsExecutable, pdfFileName, epsFile.getName());
  pb.directory(gsDir);
  pb.redirectErrorStream(true);
  Process proc = pb.start();
  final InputStream stdErrInStream = proc.getErrorStream();
  final InputStream stdOutInStream = proc.getInputStream();

  // Read the STDERR-Stream.
  String className = EpsToJpegConverter.class.getName();
  final ByteArrayOutputStream stdErrOutStream = new ByteArrayOutputStream();
  new Thread(new Runnable() {
    @Override
    public void run() {
      try {
        byte[] buf = new byte[16];
        int len = -1;
        while ((len = stdErrInStream.read(buf)) != -1) {
          stdErrOutStream.write(buf, 0, len);
        }
        stdErrFertig = true;
      } catch (IOException e) {
        log.error(e.getLocalizedMessage(), e);
      }
    }
  }, className + " Script STDERR Reader").start();

  // Read the STDOUT-Stream.
  final ByteArrayOutputStream stdOutOutStream = new ByteArrayOutputStream();
  new Thread(new Runnable() {
    @Override
    public void run() {
      try {
        byte[] buf = new byte[4096];
        int len = -1;
        while ((len = stdOutInStream.read(buf)) != -1) {
          stdOutOutStream.write(buf, 0, len);
        }
        stdOutFertig = true;
      } catch (IOException e) {
        log.error(e.getLocalizedMessage(), e);
      }
    }
  }, className + " Script STDOUT Reader").start();

  // Wait for the process to finish.
  int waitFor = proc.waitFor();
  if (waitFor != 0) {
    // If an error occured, the return code is != 0.
    // In this case wait for the reading threads to finish.
    while (!stdOutFertig || !stdErrFertig) {
      Thread.sleep(100);
    }
    throw new EpsConverterException("Das Konvertierungsscript " + gsExecutable
        + " wurde nicht erfolgreich ausgeführt.\nStandardausgabe:\n" + new String(stdOutOutStream.toByteArray())
        + "\nFehlerausgabe:\n" + new String(stdErrOutStream.toByteArray()));
  }

HTH.

Edit: Maybe the code that reads the converted image bytes back is also of interest:

  // If everything worked out ok, read the PDF.
  pdfFile = new File(gsDir, pdfFileName);
  FileInputStream pdfInStream = new FileInputStream(pdfFile);
  int len = -1;
  byte[] buf = new byte[4096];
  ByteArrayOutputStream pdfBAOS = new ByteArrayOutputStream(65535);
  while ((len = pdfInStream.read(buf)) != -1) {
    pdfBAOS.write(buf, 0, len);
  }
  pdfInStream.close();

  byte[] res = pdfBAOS.toByteArray();
  return res;
chabicht
  • 107
  • 6
  • Looks like your code assumes that the output is written to STDOUT. My problem is that FreeArc doesn't do that. It writes to a file. I therefore need to redirect it somehow. – Yrlec Oct 27 '11 at 13:11
  • What the code above does is to provide the GS executable with two filenames: the EPS input and the path where the PDF is to be written. The STDOUT and STDERR stuff is only used to print an error message if GS fails. If GS succeeded, the code (not shown) reads the file generated by GS afterwards. – chabicht Oct 27 '11 at 13:27
  • Yeah, that sort of the obvious solution. I want to solve this without writing to the file-system. That's why I want to redirect it. – Yrlec Oct 27 '11 at 13:31
  • I'm not sure if it is possible to transfer data on another way between two processes of which one is a JVM without some decent sort of API on FreeArc's side. – chabicht Oct 27 '11 at 13:37
  • Maybe you can hack something together using JNI and embedding FreeArc as a library, which might get a pain when your program is meant to be platform independent. – chabicht Oct 27 '11 at 13:42
  • In Unix you can solve it using pipes. I was hoping to be able to do the same thing on Windows using Named Pipes but I haven't found a solution yet. – Yrlec Oct 27 '11 at 13:45
  • Maybe that helps: http://stackoverflow.com/questions/634564/how-to-open-a-windows-named-pipe-from-java – chabicht Oct 27 '11 at 13:50
0

I would suggest to use ZIP compression which is part of J2SE library (see java.util.zip docs), unless there are huge performance benefits in using FreeArc (say 1 sec vs. 10 sec), or ZIP doesn't provide good enough compression. If you're keen to use FreeArc,there are several options of different complexity:

  1. Dump your input to file, run utility against it using ProcessBuilder, then transfer compressed file to output. You should use NIO channels instead of streams - that might reduce IO overhead a bit (especially for sending out). That's the only thing you can do with pure Java, and clearly not the best in terms of performance.
  2. Use JNI + native FreeArc API (if there is any, and it allows to operate on streams / byte buffers).
  3. Use JNI + native Windows API to create a named pipe that will connect your app and FreeArc, then you can work with that pretty much like with regular file.

Finally, you can reduce file IO if either input or output is a file. E.g. if you read from a file - just run FreeArc against it directly, and then send compressed file to output. Also it may not worth all that trouble if a) data size is relatively small - up to several megs maybe; or b) compression doesn't reduce it dramatically

Andrey Nudko
  • 697
  • 5
  • 5