Package tcg.print

Source Code of tcg.print.LpdLinePrinter

/**
* LpdLinePrinter Class
*
* Printer driver implementation to communicate to network printer using LDP protocol.
* See: http://lprng.sourceforge.net/LPRng-Reference-Multipart/x4882.htm
*
* LPD Protocol have severe security and usability limitation. Among other things:
* - No error messages or status capability
* - Limited or very primitive banner printing. On some systems it may be impossible
*   to turn banner printing off.
* - On most known print servers high connection activity caused by multiple systems
*   attempting to get status or spool jobs may cause catastrophic failure of the printer.
* - Connection from client must use priviledged port (instead of out of bound port).
*   This would require client to have root setuid or to run as root.
* - The default range for client port are very limited. Rapid sending of printing job
*   would be stucked after the 12th request.
*  
* For the above reasons, using RFC1179 to transfer jobs to a printer should be regarded
* as the least desirable option.
*
* Original implementation is nicked from internet. Unfortunately I don't remember where
* I got it from.
* @author    Wahyu Yoga Pratama (wyogap@thatcoolguy.com)
*
* @created      Jan 29, 2010
* @version    $$
*
* HISTORY:
* - 2010/01/29  Created.
*
*/

package tcg.print;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;

public class LpdLinePrinter implements ILinePrinter
{
  private static final int MAX_RETRIES = 30;
  private static final int DEF_SOCK_TIMEOUT_MILLIS = 500
  private static final int LPD_LISTEN_PORT = 515;
 
  private static final int LPD_MIN_CLIENT_PORT = 721;
  private static final int LPD_MAX_CLIENT_PORT = 731;
 
  private String hostname_ = "";
  private String queuename_ = "";
  private String documentName_ = "";

  private int timeoutMillis_ = DEF_SOCK_TIMEOUT_MILLIS;
  private boolean useOutOfBoundsPorts_ = false;
  private int jobNumber_ = 0;
  private boolean printRaw_ = true;

  public LpdLinePrinter(String host, String queue)
  {
    if (host != null) hostname_ = host;
    if (queue != null) queuename_ = queue;
    //use hostname as document name
    try
    {
      documentName_ = InetAddress.getLocalHost().getHostName();
    }
    catch(UnknownHostException he)
    {
      //ignore
    }
  }
 
  /**
   * By default we prints all printing data as raw binary. If you need to use
   * the text formatting of the spooler on your host set this value to false
   *
   * @param flag - whether we should print data as raw binary
   */
  public void setPrintRaw(boolean flag)
  {
    printRaw_ = flag;
  }
 
  /**
   * @see #setPrintRaw
   */
  public boolean getPrintRaw()
  {
    return(printRaw_);
  }
 
  /**
   * The RFC for lpr specifies the use of local ports numbered 721 - 731, however
   * TCP/IP also requires that any port that is used will not be released for 3 minutes
   * which means that rapid printing will get stuck on the 12th job.
   *
   * To resolve this issue you can use out of bounds ports which most print servers
   * will support
   *
   * @param flag - whether we should use out of bound port to connect to LPD server
   */
  public void setUseOutOfBoundPorts(boolean flag)
  {
    useOutOfBoundsPorts_ = flag;
  }
 
  /**
   * @see #setUseOutOfBoundPorts
   */
  public boolean getUseOutOfBoundPorts()
  {
    return(useOutOfBoundsPorts_);
  }
 
  /**
   * Ping the remote LPD server.
   *
   * @return true if we can connect to remote LPD server, false otherwise
   */
  public boolean ping()
  {
    //open connection to the print server
    Socket sock = new Socket();   
    try
    {
      sock.bind(null);
      sock.connect(new InetSocketAddress(hostname_, LPD_LISTEN_PORT), timeoutMillis_);
    }
    catch (IOException ioe)
    {
      return false;
    }
   
    //open dummy input stream and output stream
    try
    {
      sock.getOutputStream();
      sock.getInputStream();
    }
    catch (IOException ioe)
    {
      return false;
    }
   
    //close the connection
    try
    {
      sock.close();
    }
    catch(IOException ioe)
    {
      //ignore
    }
   
    //successful
    return true;
  }

  /**
   * Print a printable object
   *
   * @param printable    - the printable object
   * @return true if we can print successfully, false otherwise
   * @throws RuntimeException if there is an error
   */
  public boolean print(IPrintable printable) throws RuntimeException
  {
    //validation
    if (printable == null) return true;
   
    String controlFile = "";
    byte buffer[] = new byte[1000];
    String str;
    String strJobNumber;
   
    //generate job number. Job number cycles from 001 to 999
    if (++jobNumber_ >= 1000)
    {
      jobNumber_ = 1;
    }
    strJobNumber = "" + jobNumber_;
    while (strJobNumber.length() < 3)
    {
      strJobNumber = "0" + strJobNumber;
    }
   
    //get user name
    String username = System.getProperty("user.name");
    if (username == null)
    {
      username = "Unknown";
    }
   
    //try to open socket connection
    Socket sock = getSocket();
   
    //open input stream and output stream
    OutputStream os = null;
    InputStream is = null;
     try
    {
      os = sock.getOutputStream();
      is = sock.getInputStream();
    }
    catch (IOException ioe)
    {
      throw new RuntimeException("print: can not get data stream from remote LPD server "
                    + hostname_ + ". Error: " + ioe.getMessage());
    }

    //open printer
    try
    {
      str = "\002" + queuename_ + "\n";
      os.write(str.getBytes());
      os.flush();
    }
    catch (IOException ioe)
    {
      throw new RuntimeException("print: failed to open printer " + queuename_
          + ". Error: " + ioe.getMessage());
    }
   
    acknowledge(is, "print: failed to open printer " + queuename_);

    //build control file   
    controlFile += "H" + hostname_ + "\n";
    controlFile += "P" + username + "\n";
    controlFile += ((printRaw_) ? "o":"p") +"dfA" + strJobNumber + hostname_ + "\n";
    controlFile += "UdfA" + strJobNumber + hostname_ + "\n";
    controlFile += "N" + documentName_ + "\n";
   
    //send the length of the control file
    try
    {
      str = "\002" + (controlFile.length()) + " cfA" + strJobNumber + hostname_ + "\n";
      os.write(str.getBytes());
      os.flush();
    }
    catch (IOException ioe)
    {
      throw new RuntimeException("print: failed to send control header. Error: "
          + ioe.getMessage());
    }
   
    acknowledge(is, "print: failed to send control header");
   
    //send the control file
    try
    {
      os.write(controlFile.getBytes());
      buffer[0] = 0;
      os.write(buffer, 0, 1);
      os.flush();
    }
    catch (IOException ioe)
    {
      throw new RuntimeException("print: failed to send control file. Error: "
          + ioe.getMessage());
    }
   
    acknowledge(is, "print: failed to send control file");
   
    //get the bytes to send to print server
    byte[] data = printable.getBytes();
   
    //send the data length
    try
    {
      str = "\003" + (data.length) + " dfA" + strJobNumber + hostname_ + "\n";
      os.write(str.getBytes());
      os.flush();
    }
    catch (IOException ioe)
    {
      throw new RuntimeException("print: failed to send data header. Error: "
          + ioe.getMessage());
    }
   
    acknowledge(is, "print: failed to send data header");
   
    //send the data
    try
    {
      os.write(data, 0, data.length);
      buffer[0] = 0;
      os.write(buffer,0,1);
      os.flush();
    }
    catch (IOException ioe)
    {
      throw new RuntimeException("print: failed to send printing data. Error: "
          + ioe.getMessage());
    }
   
    acknowledge(is, "print: failed to send printing data");

    try
    {
      sock.close();
    }
    catch (IOException ioe)
    {
      //ignore
    }
   
    return true;
  }

  /**
   * Print a printable object.
   *
   * This is a hack function allowing us to call print() without worrying to catch
   * the possible runtime exception, thus simplifying the code. This should only be used
   * when we do not care what the possible failure reason is.
   *
   * @param printable    - the printable object
   * @return true if we can print successfully, false otherwise
   */
  public boolean _print(IPrintable printable)
  {
    try
    {
      return print(printable);
    }
    catch (RuntimeException re)
    {
      //ignore
      return false;
    }
  }

  private Socket getSocket() throws RuntimeException
  {
    /*
     * The LPD Protocol only allows connection from client port 721 to 731 inclusive.
     * However TCP/IP also requires that any port that is used will not be released
     * for 3 minutes which means rapid printing will be stucked on 12th job.
     *
     * The workaround is to ignore this requirement and use out of bound port.
     * (Although this does not comply with the protocol, most LPD server will support it).
     */
   
    Socket sock = null;
   
    if (useOutOfBoundsPorts_)
    {
      try
      {
        sock = new Socket(hostname_, LPD_LISTEN_PORT);
        sock.setSoTimeout(timeoutMillis_);
      }
      catch (IOException ioe)
      {
        throw new RuntimeException("getSocket: can not connect to remote LPD server "
                      + hostname_ + ". Error: " + ioe.getMessage());
      }
    }
    else
    {
      for(int j = 0; (j < MAX_RETRIES) && (sock == null); j++)
      {
        for (int i = LPD_MIN_CLIENT_PORT;
            (i <= LPD_MAX_CLIENT_PORT) && (sock == null); i++)
        {
          try
          {
            sock = new Socket(hostname_, LPD_LISTEN_PORT,
                      InetAddress.getLocalHost(), i);
            sock.setSoTimeout(timeoutMillis_);
          }
          catch (IOException ioe)
          {
            //ignore
          }
        }
        //keep trying until we get one
        if (sock == null)
        {
          try
          {
            Thread.sleep(10000);
          }
          catch(InterruptedException ie)
          {
            //ignore
          }
        }
      }  //for(int j = 0; (j < MAX_RETRIES) && (sock == null); j++)
    }
   
    if (sock == null)
    {
      throw new RuntimeException("getSocket: can not connect to remote LPD server "
          + hostname_ + ".");
    }
   
    return sock;
  }

  private void acknowledge(InputStream is, String alert) throws RuntimeException
  {
    try
    {
      if (is.read() != 0)
      {
        throw new RuntimeException(alert);
      }
    }
    catch(IOException ioe)
    {
      throw new RuntimeException(alert + ". Error: " + ioe.getMessage());
    }
  }
}
TOP

Related Classes of tcg.print.LpdLinePrinter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.