public class FileIO {
+ /** All files are read from and written to this directory */
String basePath;
+
+ // logger
private static final Logger logger = Log.getLogger("FileIO");
+ /**
+ * Constructs a new FileIO with the provided base path.
+ *
+ * @param directory Directory to use as the base path for all file
+ * operations.
+ */
public FileIO(String directory) {
basePath = directory;
}
+ /**
+ * Locates the requested file by name within our configured base path.
+ *
+ * @param key The relative file name (key)
+ * @return The File object if found.
+ */
protected File getFile(String key) {
File dir = new File(basePath);
if (!dir.exists()) {
return new File(dir, key);
}
+ /**
+ * Sets the content of a file.
+ *
+ * @param key The relative file name (key)
+ * @param text The new file content
+ *
+ * @return success or failure
+ */
public boolean set(String key, String text) {
logger.info("set => " + key);
File file = getFile(key);
return true;
}
+ /**
+ * Appends content to a file.
+ *
+ * If the file does not exist, it is first created.
+ *
+ * @param key The relative file name (key)
+ * @param text The content to append to the file
+ * @return success or failure
+ */
public boolean append(String key, String text) {
logger.info("append => " + key);
File file = getFile(key);
return true;
}
+ /**
+ * Gets the text contents of a file.
+ *
+ * @param key The relative file name (key)
+ * @return The text content of the file
+ */
public String get(String key) {
logger.info("get => " + key);
File file = getFile(key);
return buf.toString();
}
- public boolean delete(String key) {
- logger.info("delete => " + key);
+ /**
+ * Removes (deletes) a file.
+ *
+ * @param The relative file name (key)
+ * @return success or failure
+ */
+ public boolean remove(String key) {
+ logger.info("remove => " + key);
File file = getFile(key);
try {
if (file.exists() && !file.delete()) {
}
}
+ /**
+ * Returns the full list of stored keys.
+ *
+ * @return Array of relative file names
+ */
public String[] keys() {
return keys(null);
}
+ /**
+ * Returns all keys begining with the specified prefix.
+ *
+ * @param prefix The initial substring used to limit the return set
+ * of keys.
+ * @return Array of keys
+ */
public String[] keys(String prefix) {
logger.info("keys => " + prefix);
File dir = new File(basePath);
import java.io.FileInputStream;
+/**
+ * Main class for Hatch.
+ *
+ * This class operates as a two-headed beast, whose heads will occasionally
+ * communicate with each other.
+ *
+ * It runs a JavaFX thread for printing HTML documents and runs a Jetty
+ * server thread for handling communication with external clients.
+ *
+ * Most of the work performed happens solely in the Jetty server thread.
+ * Attempts to print, however, are passed into the JavaFX thread so that
+ * the HTML may be loaded into a WebView for printing, which must happen
+ * within the JavaFX thread.
+ *
+ * Messages are passed from the Jetty thread to the JavaFX thread via a
+ * blocking thread queue, observed by a separate Service thread, whose
+ * job is only to pull messages from the queue.
+ *
+ */
public class Hatch extends Application {
+ /** Browser Region for rendering and printing HTML */
private BrowserView browser;
+
+ /** BrowserView requires a stage for rendering */
private Stage primaryStage;
+
+ /** Our logger instance */
static final Logger logger = Log.getLogger("Hatch");
+ /** Message queue for passing messages from the Jetty thread into
+ * the JavaFX Application thread */
private static LinkedBlockingQueue<Map> requestQueue =
new LinkedBlockingQueue<Map>();
}
+ /**
+ * JavaFX startup call
+ */
@Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
startMsgTask();
}
+ /**
+ * Queues a message for processing by the queue processing thread.
+ */
public static void enqueueMessage(Map<String,Object> params) {
logger.debug("queueing print message");
requestQueue.offer(params);
/**
* Fire off the Service task, which checks for queued messages.
*
- * When a queued message is found, it's analyzed and passed off
- * to the correct message handler
+ * When a queued message is found, it's sent off for printing.
*/
public void startMsgTask() {
service.start();
}
+ /**
+ * Hatch main.
+ *
+ * Reads the Jetty configuration, starts the Jetty server thread,
+ * then launches the JavaFX Application thread.
+ */
public static void main(String[] args) throws Exception {
// build a server from our hatch.xml configuration file
@WebSocket
public class HatchWebSocketHandler {
+ /** A single connection to a WebSockets client */
private Session session;
+
+ /** List of Origin domains from which we allow connections */
private static String[] trustedDomains;
+
+ /** True if we trust all Origin domains */
private static boolean trustAllDomains = false;
+
+ /** Root directory for all FileIO operations */
private static String profileDirectory;
+
+ /** Our logger instance */
private static final Logger logger = Log.getLogger("WebSocketHandler");
+ /**
+ * Apply trusted domains.
+ *
+ * @param domains Array of domains which we should trust
+ */
public static void setTrustedDomains(String[] domains) {
trustedDomains = domains;
}
}
+ /**
+ * Sets the profile directory
+ *
+ * @param directory Directory path as a String
+ */
public static void setProfileDirectory(String directory) {
profileDirectory = directory;
}
/**
- * config is passed in from our WebSocketServlet container,
- * hence the public+static. Possible to access directly?
+ * Runs the initial, global configuration for this handler.
+ * TODO: move this into setProfileDirectory() (which will need to
+ * be force-called regardless of config)?
*/
- //public static void configure(ServletConfig config) {
public static void configure() {
logger.info("WebSocketHandler.configure()");
}
}
+ /**
+ * Compares the Origin of the current WebSocket connection to the list
+ * of allowed domains to determine if the current connection should
+ * be allowed.
+ *
+ * @return True if the Origin domain is allowed, false otherwise.
+ */
protected boolean verifyOriginDomain() {
logger.info("received connection from IP " +
session.getRemoteAddress().getAddress());
}
+ /**
+ * WebSocket onConnect handler.
+ *
+ * Verify the Origin domain before any communication may take place
+ */
@OnWebSocketConnect
public void onConnect(Session session) {
this.session = session;
if (!verifyOriginDomain()) session.close();
}
+ /**
+ * WebSocket onClose handler.
+ *
+ * Clears our current session.
+ */
@OnWebSocketClose
public void onClose(int statusCode, String reason) {
logger.info("onClose() statusCode=" + statusCode + ", reason=" + reason);
this.session = null;
}
+ /**
+ * Send a message to our connected client.
+ *
+ * @param json A JSON-encodable object to send to the caller.
+ * @param msgid The message identifier
+ */
protected void reply(Object json, Long msgid) {
reply(json, msgid, true);
}
+ /**
+ * Send a message to our connected client.
+ *
+ * @param json A JSON-encodable object to send to the caller.
+ * @param msgid The message identifier
+ * @param success If false, the response will be packaged as an error
+ * message.
+ */
protected void reply(Object json, Long msgid, boolean success) {
Map<String, Object> response = new HashMap<String, Object>();
}
}
+ /**
+ * WebSocket onMessage handler.
+ *
+ * Processes the incoming message and passes the request off to the
+ * necessary handler. Messages must be encoded as JSON strings.
+ */
@OnWebSocketMessage
@SuppressWarnings("unchecked") // direct casting JSON-parsed objects
public void onMessage(String message) {
if (action.equals("remove")) {
io = new FileIO(profileDirectory);
- if (io.delete(key)) {
+ if (io.remove(key)) {
reply("Removal of " + key + " successful", msgid);
} else {
reply("Removal of " + key + " failed", msgid, false);
*/
package org.evergreen_ils.hatch;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
import javax.servlet.annotation.WebServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
+/**
+ * Links HatchWebSocketHandler in as a Servlet handler.
+ */
public class HatchWebSocketServlet extends WebSocketServlet {
- static final Logger logger = Log.getLogger("WebSocketServlet");
-
@Override
public void configure(WebSocketServletFactory factory) {
factory.register(HatchWebSocketHandler.class);
public class PrintManager {
+ /** Our logger instance */
static final Logger logger = Log.getLogger("PrintManager");
/**
* Shows the print dialog, allowing the user to modify settings,
* but performs no print.
+ *
+ * @param params Print request parameters. This is the top-level
+ * request object, containing the action, etc. Within 'params'
+ * will be a sub-object under the "config" key, which contains
+ * the Printer configuration options (if any are already set).
+ * @return A Map of printer settings extracted from the print dialog.
*/
public Map<String,Object> configurePrinter(
Map<String,Object> params) throws IllegalArgumentException {
/**
* Print the requested page using the provided settings
+ *
+ * @param engine The WebEngine instance to print
+ * @param params Print request parameters
*/
public void print(WebEngine engine, Map<String,Object>params) {
}
/**
- * Constructs a PrinterJob based on the provided settings
+ * Constructs a PrinterJob based on the provided settings.
+ *
+ * @param settings The printer configuration Map.
+ * @return The newly created printer job.
*/
public PrinterJob buildPrinterJob(
Map<String,Object> settings) throws IllegalArgumentException {
/**
* Builds a PageLayout for the requested printer, using the
* provided settings.
+ *
+ * @param settings The printer configuration settings
+ * @param printer The printer from which to spawn the PageLayout
+ * @return The newly constructed PageLayout object.
*/
protected PageLayout buildPageLayout(
Map<String,Object> settings, Printer printer) {
/**
* Applies the provided settings to the PrinterJob.
+ *
+ * @param settings The printer configuration settings map.
+ * @param job A PrinterJob, constructed from buildPrinterJob()
*/
protected void applySettingsToJob(
Map<String,Object> settings, PrinterJob job) {
/**
* Extracts and flattens the various configuration values from a
* PrinterJob and its associated printer and stores the values in a Map.
+ *
+ * @param job The PrinterJob whose attributes are to be extracted.
+ * @return The extracted printer settings map.
*/
protected Map<String,Object> extractSettingsFromJob(PrinterJob job) {
Map<String,Object> settings = new HashMap<String,Object>();
}
/**
- * Returns a list of all known Printer's
+ * Returns all known Printer's.
+ *
+ * @return Array of all printers
*/
protected Printer[] getPrinters() {
ObservableSet<Printer> printerObserver = Printer.getAllPrinters();
/**
* Returns a list of all known printers, with their attributes
* encoded as a simple key/value Map.
+ *
+ * @return Map of printer information.
*/
protected List<Map<String,Object>> getPrintersAsMaps() {
Printer[] printers = getPrinters();
/**
* Returns the Printer with the specified name.
+ *
+ * @param name The printer name
+ * @return The printer whose name matches the provided name, or null
+ * if no such printer is found.
*/
protected Printer getPrinterByName(String name) {
Printer[] printers = getPrinters();